tor-commits
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
October 2014
- 26 participants
- 1551 discussions

[translation/liveusb-creator_completed] Update translations for liveusb-creator_completed
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit 4b4a4e2b92fb7fd7b8ee733903e347aee00e66dc
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 09:45:36 2014 +0000
Update translations for liveusb-creator_completed
---
sl_SI/sl_SI.po | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 575 insertions(+)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
new file mode 100644
index 0000000..c177287
--- /dev/null
+++ b/sl_SI/sl_SI.po
@@ -0,0 +1,575 @@
+# SOME DESCRIPTIVE TITLE.
+…
[View More]# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Dušan <dusan.k(a)zoho.com>, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: The Tor Project\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-10-15 16:05+0200\n"
+"PO-Revision-Date: 2014-10-19 09:40+0000\n"
+"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
+"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: sl_SI\n"
+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
+
+#: ../liveusb/dialog.py:150 ../liveusb/launcher_ui.py:149
+#, python-format
+msgid "%(distribution)s Installer"
+msgstr "%(distribution)s Namestitev"
+
+#: ../liveusb/gui.py:773
+#, python-format
+msgid "%(filename)s selected"
+msgstr "%(filename)s izbrana"
+
+#: ../liveusb/gui.py:423
+#, python-format
+msgid "%(size)s %(label)s"
+msgstr "%(size)s %(label)s"
+
+#: ../liveusb/gui.py:429
+#, python-format
+msgid "%(vendor)s %(model)s (%(details)s) - %(device)s"
+msgstr "%(vendor)s %(model)s (%(details)s) - %(device)s"
+
+#: ../liveusb/creator.py:1059
+#, python-format
+msgid "%s already bootable"
+msgstr "%s že zagonska"
+
+#: ../liveusb/launcher_ui.py:156
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
+"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Need help? Read the </span><a href=\"file:///usr/share/doc/tails/website/doc/first_steps/installation.en.html\"><span style=\" text-decoration: underline; color:#0000ff;\">documentation</span></a><span style=\" font-size:11pt;\">.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Rabite pomoč? Preberite</span><a href=\"file:///usr/share/doc/tails/website/doc/first_steps/installation.en.html\"><span style=\" text-decoration: underline; color:#0000ff;\">dokumentacijo</span></a><span style=\" font-size:11pt;\">.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:151
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Copy the running Tails onto a USB stick or SD card. All data on the target drive will be lost.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Skopiraj Sled na USB ključ ali SD kartico. Vsi podatki na nosilcu bodo izgubljeni.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:153
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Copy the running Tails onto an already installed Tails device. Other partitions found on the stick are preserved.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Skopiraj tekočo Sled na že nameščeno napravo. Ostale najdene particije na ključu so ohranjene.</span></p></body></html>"
+
+#: ../liveusb/launcher_ui.py:155
+msgid ""
+"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+"p, li { white-space: pre-wrap; }\n"
+"</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
+"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Upgrade an already installed Tails device from a new ISO image.</span></p></body></html>"
+msgstr "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:11pt;\">Nadgradite že nameščeno Sledilno napravo z novo ISO sliko.</span></p></body></html>"
+
+#: ../liveusb/dialog.py:154
+msgid "Alt+B"
+msgstr "Alt+B"
+
+#: ../liveusb/dialog.py:153
+msgid "Browse"
+msgstr "Brskanje"
+
+#: ../liveusb/dialog.py:160
+msgid ""
+"By allocating extra space on your USB stick for a persistent overlay, you "
+"will be able to store data and make permanent modifications to your live "
+"operating system. Without it, you will not be able to save data that will "
+"persist after a reboot."
+msgstr "Z dodelitvijo dodatnega prostora na vašem USB ključu za nenehno prikrivanje, boste lahko shranjevali podatke in trajne spremembe vašega OS. Brez njega ne boste mogli shraniti podatkov za nadaljevanje pri ponovnem zagonu računalnika."
+
+#: ../liveusb/creator.py:1158 ../liveusb/creator.py:1421
+#, python-format
+msgid "Calculating the SHA1 of %s"
+msgstr "Preračunavanje SHA1 od %s"
+
+#: ../liveusb/creator.py:1369
+msgid "Cannot find"
+msgstr "Ne najdem"
+
+#: ../liveusb/creator.py:556
+#, python-format
+msgid "Cannot find device %s"
+msgstr "Ne najdem naprave %s"
+
+#: ../liveusb/launcher_ui.py:150
+msgid ""
+"Clone\n"
+"&&\n"
+"Install"
+msgstr "Kloniraj\n&&\nNamesti"
+
+#: ../liveusb/launcher_ui.py:152
+msgid ""
+"Clone\n"
+"&&\n"
+"Upgrade"
+msgstr "Kloniraj\n&&\nNadgradi"
+
+#: ../liveusb/creator.py:408
+#, python-format
+msgid "Creating %sMB persistent overlay"
+msgstr "Ustvarjanje %s nenehnega prikrivanja"
+
+#: ../liveusb/gui.py:551
+msgid ""
+"Device is not yet mounted, so we cannot determine the amount of free space."
+msgstr "Naprava še ni priklopljena zato ne moremo določiti količino prostega."
+
+#: ../liveusb/dialog.py:157
+#, python-format
+msgid "Download %(distribution)s"
+msgstr "Prenos %(distribution)s"
+
+#: ../liveusb/gui.py:747
+msgid "Download complete!"
+msgstr "Prenos končan!"
+
+#: ../liveusb/gui.py:751
+msgid "Download failed: "
+msgstr "Neuspel prenos:"
+
+#: ../liveusb/gui.py:88
+#, python-format
+msgid "Downloading %s..."
+msgstr "Prenašanje %s"
+
+#: ../liveusb/creator.py:1154
+msgid "Drive is a loopback, skipping MBR reset"
+msgstr "Pogon je v povratni zanki, preskoči reset MBR"
+
+#: ../liveusb/creator.py:827
+#, python-format
+msgid "Entering unmount_device for '%(device)s'"
+msgstr "Vnašanje odklopljene_naprave za '%(device)s'"
+
+#: ../liveusb/creator.py:1234
+msgid "Error probing device"
+msgstr "Napaka sondirane naprave"
+
+#: ../liveusb/gui.py:211
+msgid ""
+"Error: Cannot set the label or obtain the UUID of your device. Unable to "
+"continue."
+msgstr "Napaka: Ne morem nastaviti etiketo ali pridobiti UUID naprave. Nemogoče nadaljevanje."
+
+#: ../liveusb/creator.py:384
+msgid ""
+"Error: The SHA1 of your Live CD is invalid. You can run this program with "
+"the --noverify argument to bypass this verification check."
+msgstr "Napaka: SHA1 vašega CD-ja je neveljavna. Lahko zaženete ta program z --noverify argumentom,ki zaobide preverjanje verifikacije."
+
+#: ../liveusb/creator.py:147
+msgid "Extracting live image to the target device..."
+msgstr "Izpisovanje tekoče slike na ciljno napravo..."
+
+#: ../liveusb/creator.py:1102
+#, python-format
+msgid "Formatting %(device)s as FAT32"
+msgstr "Formatiranje %(device)s kot FAT32"
+
+#: ../liveusb/creator.py:142
+msgid "ISO MD5 checksum passed"
+msgstr "ISO MD5 preverjanje opravljeno"
+
+#: ../liveusb/creator.py:140
+msgid "ISO MD5 checksum verification failed"
+msgstr "ISO MD5 preverjanje neuspešno "
+
+#: ../liveusb/dialog.py:156
+msgid ""
+"If you do not select an existing Live ISO, the selected release will be "
+"downloaded for you."
+msgstr "Če ne boste izbrali obstoječe Live ISO, bo za vas sneta izbrana izdaja."
+
+#: ../liveusb/dialog.py:165
+msgid "Install Tails"
+msgstr "Namesti Sledi"
+
+#: ../liveusb/gui.py:614
+msgid "Installation complete!"
+msgstr "Namestitev končana!"
+
+#: ../liveusb/gui.py:264
+#, python-format
+msgid "Installation complete! (%s)"
+msgstr "Namestitev končana! (%s)"
+
+#: ../liveusb/gui.py:615
+msgid "Installation was completed. Press OK to close this program."
+msgstr "Namestitev končana. Pritisnite VREDU za zapiranje programa"
+
+#: ../liveusb/creator.py:959 ../liveusb/creator.py:1275
+msgid "Installing bootloader..."
+msgstr "Namestitev zaganjalnika ..."
+
+#: ../liveusb/gui.py:269
+msgid "LiveUSB creation failed!"
+msgstr "Kreiranje LiveUSB ni uspelo!"
+
+#: ../liveusb/creator.py:1370
+msgid ""
+"Make sure to extract the entire liveusb-creator zip file before running this"
+" program."
+msgstr "Prepričajte se, da je izluščena celotna liveusb-creator zip datoteko, preden poženete ta program."
+
+#: ../liveusb/creator.py:1246
+msgid ""
+"Make sure your USB key is plugged in and formatted with the FAT filesystem"
+msgstr "Prepričajte se, da je USB vtaknjen in formatiran z FAT sistemsko datoteko"
+
+#: ../liveusb/creator.py:849
+#, python-format
+msgid "Mount %s exists after unmounting"
+msgstr "Priklop %s obstaja po odklopu"
+
+#: ../liveusb/gui.py:557
+#, python-format
+msgid "No free space on device %(device)s"
+msgstr "Na napravi ni prostora %(device)s"
+
+#: ../liveusb/creator.py:816
+msgid "No mount points found"
+msgstr "Ne najdem priklopne točke"
+
+#: ../liveusb/creator.py:401
+msgid "Not enough free space on device."
+msgstr "Ni dovolj prostora na napravi."
+
+#: ../liveusb/gui.py:538
+msgid "Partition is FAT16; Restricting overlay size to 2G"
+msgstr "Particija je FAT 16; Omejevanje na velikodt 2G"
+
+#: ../liveusb/gui.py:534
+msgid "Partition is FAT32; Restricting overlay size to 4G"
+msgstr "Particija je FAT 32; Omejevanje na velikost 4G"
+
+#: ../liveusb/creator.py:228 ../liveusb/creator.py:856
+#, python-format
+msgid "Partitioning device %(device)s"
+msgstr "Delitev naprave %(device)s"
+
+#: ../liveusb/gui.py:605
+msgid "Persistent Storage"
+msgstr "Trajno shranjevanje"
+
+#: ../liveusb/dialog.py:161
+msgid "Persistent Storage (0 MB)"
+msgstr "Trajno shranjevanje (0 MB)"
+
+#: ../liveusb/gui.py:667 ../liveusb/gui.py:696
+msgid "Please confirm your device selection"
+msgstr "Prosim, potrdite izbiro naprave"
+
+#: ../liveusb/gui.py:451
+msgid "Refreshing releases..."
+msgstr "Osveževanje izdaje..."
+
+#: ../liveusb/gui.py:456
+msgid "Releases updated!"
+msgstr "Izdaje posodobljene!"
+
+#: ../liveusb/creator.py:966 ../liveusb/creator.py:1293
+#, python-format
+msgid "Removing %(file)s"
+msgstr "Odstranjevanje %(file)s"
+
+#: ../liveusb/creator.py:483
+msgid "Removing existing Live OS"
+msgstr "Odstranitev obstoječe žive distribucije OS"
+
+#: ../liveusb/creator.py:1148
+#, python-format
+msgid "Resetting Master Boot Record of %s"
+msgstr "Ponastavitev MBR %s"
+
+#: ../liveusb/gui.py:758
+msgid "Select Live ISO"
+msgstr "Izberite živo distribucijo ISO"
+
+#: ../liveusb/creator.py:184
+msgid "Setting up OLPC boot file..."
+msgstr "Nastavitev OLPC zagonske datoteke..."
+
+#: ../liveusb/creator.py:730
+#, python-format
+msgid ""
+"Some partitions of the target device %(device)s are mounted. They will be "
+"unmounted before starting the installation process."
+msgstr "Nekatere particije na ciljni napravi %(device)s so vpete. Potrebno jih je odpeti pred začetkom namestitve. "
+
+#: ../liveusb/creator.py:133
+msgid ""
+"Source type does not support verification of ISO MD5 checksum, skipping"
+msgstr "Tip izvora ne podpira kontrole ISO MD5, preskoči"
+
+#: ../liveusb/creator.py:1182
+msgid "Synchronizing data on disk..."
+msgstr "Sinhronizacija podatkov na disk"
+
+#: ../liveusb/dialog.py:159
+msgid "Target Device"
+msgstr "Ciljna naprava"
+
+#: ../liveusb/gui.py:761
+msgid ""
+"The selected file is unreadable. Please fix its permissions or select "
+"another file."
+msgstr "Izbrana datoteka je nečitljiva. Popravite dovoljenje ali izberite drugo datoteko."
+
+#: ../liveusb/creator.py:345
+#, python-format
+msgid ""
+"There was a problem executing the following command: `%(command)s`.\n"
+"A more detailed error log has been written to '%(filename)s'."
+msgstr "Nastal je problem pri izvrševanju ukaza: `%(command)s`.\nZa prikaz podrobnosti je bila ustvarjena log datoteka v '%(filename)s'."
+
+#: ../liveusb/dialog.py:151
+msgid ""
+"This button allows you to browse for an existing Live system ISO that you "
+"have previously downloaded. If you do not select one, a release will be "
+"downloaded for you automatically."
+msgstr "Ta gumb vam omogoča brskanje za obstoječimi živimi sistemi ISO, ki ste jih že prenesli. Če ne boste izbrali katerega, bo izdaja prenesena samodejno."
+
+#: ../liveusb/dialog.py:164
+msgid ""
+"This button will begin the LiveUSB creation process. This entails "
+"optionally downloading a release (if an existing one wasn't selected), "
+"extracting the ISO to the USB device, creating the persistent overlay, and "
+"installing the bootloader."
+msgstr "Ta gumb prične s procesom ustvarjanja LiveUSB. To bo sprožilo prenašanje izdaje (če obstoječa ni bila izbrana ), razpakiranje ISO na USB ključek, ustvarilo trajno prikrivanje in namestilo zaganjalnik."
+
+#: ../liveusb/dialog.py:158
+msgid ""
+"This is the USB stick that you want to install your Live system on. This "
+"device must be formatted with the FAT filesystem."
+msgstr "To je USB ključek na katerega želite namestiti živ sistem. Naprava mora biti formatirana z FAT datotečnim sistemom."
+
+#: ../liveusb/dialog.py:163
+msgid ""
+"This is the progress bar that will indicate how far along in the LiveUSB "
+"creation process you are"
+msgstr "To je vrstica napredka, ki kaže kako daleč ste v procesu ustvarjanja LiveUSB "
+
+#: ../liveusb/dialog.py:162
+msgid "This is the status console, where all messages get written to."
+msgstr "To je konzola stanja, kjer se zapisujejo vsa sporočila."
+
+#: ../liveusb/creator.py:922
+msgid "Trying to continue anyway."
+msgstr "Poskušam vseeno nadaljevati."
+
+#: ../liveusb/creator.py:954
+#, python-format
+msgid "Unable to change volume label: %(message)s"
+msgstr "Nemogoče spremeniti nalepko nosilca: %(message)s"
+
+#: ../liveusb/creator.py:492 ../liveusb/creator.py:503
+#, python-format
+msgid "Unable to chmod %(file)s: %(message)s"
+msgstr "Nemogoče v chmod %(file)s: %(message)s"
+
+#: ../liveusb/creator.py:469
+#, python-format
+msgid "Unable to copy %(infile)s to %(outfile)s: %(message)s"
+msgstr "Nemogoče kopirati %(infile)s v %(outfile)s: %(message)s"
+
+#: ../liveusb/gui.py:402
+msgid "Unable to find any USB drive"
+msgstr "Ne najde nobenega USB pogona"
+
+#: ../liveusb/creator.py:1236
+msgid "Unable to find any supported device"
+msgstr "Ne najde nobene podprte naprave"
+
+#: ../liveusb/creator.py:1079
+msgid "Unable to find partition"
+msgstr "Ne najde particije"
+
+#: ../liveusb/creator.py:1316
+msgid ""
+"Unable to get Win32_LogicalDisk; win32com query did not return any results"
+msgstr "Nemogoče dobiti Win32_LogicalDisk; win32com poizvedba ni vrnila nobenih rezultatov"
+
+#: ../liveusb/gui.py:660
+msgid "Unable to mount device"
+msgstr "Nemogoče priklopiti napravo"
+
+#: ../liveusb/creator.py:804
+#, python-format
+msgid "Unable to mount device: %(message)s"
+msgstr "Nemogoče priklopiti napravo: %(message)s"
+
+#: ../liveusb/creator.py:508
+#, python-format
+msgid "Unable to remove directory from previous LiveOS: %(message)s"
+msgstr "Nemogoče odstraniti imenik predhodne LiveOS: %(message)s"
+
+#: ../liveusb/creator.py:496
+#, python-format
+msgid "Unable to remove file from previous LiveOS: %(message)s"
+msgstr "Nemogoče odstraniti datoteke predhodnje LiveOS: %(message)s"
+
+#: ../liveusb/creator.py:1151
+msgid ""
+"Unable to reset MBR. You may not have the `syslinux` package installed."
+msgstr "Nemogoče ponastaviti MBR. Nimate nameščenega paketa `syslinux`"
+
+#: ../liveusb/gui.py:767
+msgid ""
+"Unable to use the selected file. You may have better luck if you move your "
+"ISO to the root of your drive (ie: C:\\)"
+msgstr "Nemogoče uporabiti izbrano datoteko. Več sreče boste imeli s premikom vaše ISO v koren pogona (npr.: C:\\ )"
+
+#: ../liveusb/creator.py:711
+#, python-format
+msgid "Unable to write on %(device)s, skipping."
+msgstr "Nemogoče zapisovanje na %(device)s, preskočim."
+
+#: ../liveusb/creator.py:390
+msgid "Unknown ISO, skipping checksum verification"
+msgstr "Nepoznan ISO, preskočim preverjanje"
+
+#: ../liveusb/creator.py:800
+#, python-format
+msgid "Unknown dbus exception while trying to mount device: %(message)s"
+msgstr "Nepoznana dbus izjema pri priklapljanju naprave: %(message)s"
+
+#: ../liveusb/creator.py:779 ../liveusb/creator.py:933
+msgid "Unknown filesystem. Your device may need to be reformatted."
+msgstr "Nepoznan datotečni sistem. Naprava mora biti formatirana."
+
+#: ../liveusb/gui.py:84
+#, python-format
+msgid "Unknown release: %s"
+msgstr "Nepoznana izdaja: %s"
+
+#: ../liveusb/creator.py:841
+#, python-format
+msgid "Unmounting '%(udi)s' on '%(device)s'"
+msgstr "Odklapljanje '%(udi)s' na '%(device)s'"
+
+#: ../liveusb/creator.py:837
+#, python-format
+msgid "Unmounting mounted filesystems on '%(device)s'"
+msgstr "Odklapljanje priklopljenega datotečnega sistema na '%(device)s'"
+
+#: ../liveusb/creator.py:919
+#, python-format
+msgid "Unsupported device '%(device)s', please report a bug."
+msgstr "Nepodprta naprava '%(device)s', prosim javite hrošča."
+
+#: ../liveusb/creator.py:784 ../liveusb/creator.py:936
+#, python-format
+msgid "Unsupported filesystem: %s"
+msgstr "Nepodprt datotečni sistem: %s"
+
+#: ../liveusb/creator.py:782
+#, python-format
+msgid ""
+"Unsupported filesystem: %s\n"
+"In case you are trying to upgrade a manually installed Tails system (that is, if it was installed without this installer), this option is not supported: you need to install it anew to start with, e.g. by choosing the \"Clone & Install\" action instead."
+msgstr "Nepodprt datotečni sistem: %s\nV slučaju, da poskušate ročno namestiti sledilni sistem ( to je, če ni bil nameščen s tem zaganjalnikom ), ta opcija ni podprta: namestiti ga morate ponovno z npr.: izbiro \"Clone & Install\" dejanjem."
+
+#: ../liveusb/creator.py:1249
+#, python-format
+msgid ""
+"Unsupported filesystem: %s\n"
+"Please backup and format your USB key with the FAT filesystem."
+msgstr "Nepodprt datotečni sistem: %s\nProsim formatirajte USB z FAT datotečnim sistemom."
+
+#: ../liveusb/creator.py:882
+#, python-format
+msgid "Updating properties of system partition %(system_partition)s"
+msgstr "Posodabljanje lastnosti sistemske particije %(system_partition)s"
+
+#: ../liveusb/launcher_ui.py:154
+msgid "Upgrade from ISO"
+msgstr "Posodabljanje iz ISO"
+
+#: ../liveusb/dialog.py:152
+msgid "Use existing Live system ISO"
+msgstr "Uporabite obstoječ živ sistem ISO"
+
+#: ../liveusb/creator.py:135
+msgid "Verifying ISO MD5 checksum"
+msgstr "Preverjanje ISO MD5 "
+
+#: ../liveusb/creator.py:364
+msgid "Verifying SHA1 checksum of LiveCD image..."
+msgstr "Preverjanje SHA1 kontrolne vsote na LiveCD sliki..."
+
+#: ../liveusb/creator.py:368
+msgid "Verifying SHA256 checksum of LiveCD image..."
+msgstr "Preverjanje SHA256 kontrolne vsote na LiveCD sliki..."
+
+#: ../liveusb/creator.py:930 ../liveusb/creator.py:1242
+msgid "Verifying filesystem..."
+msgstr "Preverjanje datotečnega sistema"
+
+#: ../liveusb/gui.py:694
+msgid ""
+"Warning: Creating a new persistent overlay will delete your existing one."
+msgstr "Opozorilo: Ustvarjanje novega prikrivanja bo izbrisalo obstoječega."
+
+#: ../liveusb/gui.py:376
+msgid ""
+"Warning: This tool needs to be run as an Administrator. To do this, right "
+"click on the icon and open the Properties. Under the Compatibility tab, "
+"check the \"Run this program as an administrator\" box."
+msgstr "Opozorilo: To orodje mora biti zagnano kot Administrator. Da to naredite, kliknite desno na ikono in odprite lastnosti. V tab-u združljivost označite \" Zaženi kot Administrator\"."
+
+#: ../liveusb/creator.py:154
+#, python-format
+msgid "Wrote to device at %(speed)d MB/sec"
+msgstr "Zapisano na napravo s %(speed)d MB/sec"
+
+#: ../liveusb/gui.py:668
+#, python-format
+msgid ""
+"You are going to install Tails on the %(size)s %(vendor)s %(model)s device "
+"(%(device)s). All data on the selected device will be lost. Continue?"
+msgstr "Namestili boste Sledi na %(size)s %(vendor)s %(model)s napravo (%(device)s). Vsi podatki na izbrani napravi bodo izgubljeni. Nadaljujem?"
+
+#: ../liveusb/gui.py:684
+#, python-format
+msgid ""
+"You are going to upgrade Tails on the %(parent_size)s %(vendor)s %(model)s "
+"device (%(device)s). Any persistent volume on this device will remain "
+"unchanged. Continue?"
+msgstr "Nadgradili boste Sledi na %(parent_size)s %(vendor)s %(model)s napravi (%(device)s). Noben trajni nosilec na tej napravi ne bo spremenjen. Nadaljujem?"
+
+#: ../liveusb/creator.py:618
+msgid ""
+"You are using an old version of syslinux-extlinux that does not support the "
+"ext4 filesystem"
+msgstr "Uporabljate staro verzijo syslinux-extlinux, ki ne podpira ext4 datotečnega sistema"
+
+#: ../liveusb/gui.py:752
+msgid "You can try again to resume your download"
+msgstr "Ponovno poskusite nadaljevati vaš prenos"
+
+#: ../liveusb/creator.py:94
+msgid "You must run this application as root"
+msgstr "To aplikacijo morate zagnati kot root"
+
+#: ../liveusb/dialog.py:155
+msgid "or"
+msgstr "ali"
[View Less]
1
0

[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit ac5dea05e4ca4331d0a8be0c42336939639832ef
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 09:45:32 2014 +0000
Update translations for liveusb-creator
---
sl_SI/sl_SI.po | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index f1c4b15..c177287 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"…
[View More]Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-15 16:05+0200\n"
-"PO-Revision-Date: 2014-10-19 09:14+0000\n"
+"PO-Revision-Date: 2014-10-19 09:40+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -406,7 +406,7 @@ msgstr "Ne najde particije"
#: ../liveusb/creator.py:1316
msgid ""
"Unable to get Win32_LogicalDisk; win32com query did not return any results"
-msgstr ""
+msgstr "Nemogoče dobiti Win32_LogicalDisk; win32com poizvedba ni vrnila nobenih rezultatov"
#: ../liveusb/gui.py:660
msgid "Unable to mount device"
@@ -514,39 +514,39 @@ msgstr "Preverjanje ISO MD5 "
#: ../liveusb/creator.py:364
msgid "Verifying SHA1 checksum of LiveCD image..."
-msgstr ""
+msgstr "Preverjanje SHA1 kontrolne vsote na LiveCD sliki..."
#: ../liveusb/creator.py:368
msgid "Verifying SHA256 checksum of LiveCD image..."
-msgstr ""
+msgstr "Preverjanje SHA256 kontrolne vsote na LiveCD sliki..."
#: ../liveusb/creator.py:930 ../liveusb/creator.py:1242
msgid "Verifying filesystem..."
-msgstr ""
+msgstr "Preverjanje datotečnega sistema"
#: ../liveusb/gui.py:694
msgid ""
"Warning: Creating a new persistent overlay will delete your existing one."
-msgstr ""
+msgstr "Opozorilo: Ustvarjanje novega prikrivanja bo izbrisalo obstoječega."
#: ../liveusb/gui.py:376
msgid ""
"Warning: This tool needs to be run as an Administrator. To do this, right "
"click on the icon and open the Properties. Under the Compatibility tab, "
"check the \"Run this program as an administrator\" box."
-msgstr ""
+msgstr "Opozorilo: To orodje mora biti zagnano kot Administrator. Da to naredite, kliknite desno na ikono in odprite lastnosti. V tab-u združljivost označite \" Zaženi kot Administrator\"."
#: ../liveusb/creator.py:154
#, python-format
msgid "Wrote to device at %(speed)d MB/sec"
-msgstr ""
+msgstr "Zapisano na napravo s %(speed)d MB/sec"
#: ../liveusb/gui.py:668
#, python-format
msgid ""
"You are going to install Tails on the %(size)s %(vendor)s %(model)s device "
"(%(device)s). All data on the selected device will be lost. Continue?"
-msgstr ""
+msgstr "Namestili boste Sledi na %(size)s %(vendor)s %(model)s napravo (%(device)s). Vsi podatki na izbrani napravi bodo izgubljeni. Nadaljujem?"
#: ../liveusb/gui.py:684
#, python-format
@@ -554,22 +554,22 @@ msgid ""
"You are going to upgrade Tails on the %(parent_size)s %(vendor)s %(model)s "
"device (%(device)s). Any persistent volume on this device will remain "
"unchanged. Continue?"
-msgstr ""
+msgstr "Nadgradili boste Sledi na %(parent_size)s %(vendor)s %(model)s napravi (%(device)s). Noben trajni nosilec na tej napravi ne bo spremenjen. Nadaljujem?"
#: ../liveusb/creator.py:618
msgid ""
"You are using an old version of syslinux-extlinux that does not support the "
"ext4 filesystem"
-msgstr ""
+msgstr "Uporabljate staro verzijo syslinux-extlinux, ki ne podpira ext4 datotečnega sistema"
#: ../liveusb/gui.py:752
msgid "You can try again to resume your download"
-msgstr ""
+msgstr "Ponovno poskusite nadaljevati vaš prenos"
#: ../liveusb/creator.py:94
msgid "You must run this application as root"
-msgstr ""
+msgstr "To aplikacijo morate zagnati kot root"
#: ../liveusb/dialog.py:155
msgid "or"
-msgstr ""
+msgstr "ali"
[View Less]
1
0

[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit c260c8adfca8d3d43ac2f8c04ea4d0599bf6eb47
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 09:15:36 2014 +0000
Update translations for liveusb-creator
---
sl_SI/sl_SI.po | 48 ++++++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index a11f9bc..f1c4b15 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The …
[View More]Tor Project\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-15 16:05+0200\n"
-"PO-Revision-Date: 2014-10-19 08:44+0000\n"
+"PO-Revision-Date: 2014-10-19 09:14+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -397,11 +397,11 @@ msgstr "Ne najde nobenega USB pogona"
#: ../liveusb/creator.py:1236
msgid "Unable to find any supported device"
-msgstr ""
+msgstr "Ne najde nobene podprte naprave"
#: ../liveusb/creator.py:1079
msgid "Unable to find partition"
-msgstr ""
+msgstr "Ne najde particije"
#: ../liveusb/creator.py:1316
msgid ""
@@ -410,107 +410,107 @@ msgstr ""
#: ../liveusb/gui.py:660
msgid "Unable to mount device"
-msgstr ""
+msgstr "Nemogoče priklopiti napravo"
#: ../liveusb/creator.py:804
#, python-format
msgid "Unable to mount device: %(message)s"
-msgstr ""
+msgstr "Nemogoče priklopiti napravo: %(message)s"
#: ../liveusb/creator.py:508
#, python-format
msgid "Unable to remove directory from previous LiveOS: %(message)s"
-msgstr ""
+msgstr "Nemogoče odstraniti imenik predhodne LiveOS: %(message)s"
#: ../liveusb/creator.py:496
#, python-format
msgid "Unable to remove file from previous LiveOS: %(message)s"
-msgstr ""
+msgstr "Nemogoče odstraniti datoteke predhodnje LiveOS: %(message)s"
#: ../liveusb/creator.py:1151
msgid ""
"Unable to reset MBR. You may not have the `syslinux` package installed."
-msgstr ""
+msgstr "Nemogoče ponastaviti MBR. Nimate nameščenega paketa `syslinux`"
#: ../liveusb/gui.py:767
msgid ""
"Unable to use the selected file. You may have better luck if you move your "
"ISO to the root of your drive (ie: C:\\)"
-msgstr ""
+msgstr "Nemogoče uporabiti izbrano datoteko. Več sreče boste imeli s premikom vaše ISO v koren pogona (npr.: C:\\ )"
#: ../liveusb/creator.py:711
#, python-format
msgid "Unable to write on %(device)s, skipping."
-msgstr ""
+msgstr "Nemogoče zapisovanje na %(device)s, preskočim."
#: ../liveusb/creator.py:390
msgid "Unknown ISO, skipping checksum verification"
-msgstr ""
+msgstr "Nepoznan ISO, preskočim preverjanje"
#: ../liveusb/creator.py:800
#, python-format
msgid "Unknown dbus exception while trying to mount device: %(message)s"
-msgstr ""
+msgstr "Nepoznana dbus izjema pri priklapljanju naprave: %(message)s"
#: ../liveusb/creator.py:779 ../liveusb/creator.py:933
msgid "Unknown filesystem. Your device may need to be reformatted."
-msgstr ""
+msgstr "Nepoznan datotečni sistem. Naprava mora biti formatirana."
#: ../liveusb/gui.py:84
#, python-format
msgid "Unknown release: %s"
-msgstr ""
+msgstr "Nepoznana izdaja: %s"
#: ../liveusb/creator.py:841
#, python-format
msgid "Unmounting '%(udi)s' on '%(device)s'"
-msgstr ""
+msgstr "Odklapljanje '%(udi)s' na '%(device)s'"
#: ../liveusb/creator.py:837
#, python-format
msgid "Unmounting mounted filesystems on '%(device)s'"
-msgstr ""
+msgstr "Odklapljanje priklopljenega datotečnega sistema na '%(device)s'"
#: ../liveusb/creator.py:919
#, python-format
msgid "Unsupported device '%(device)s', please report a bug."
-msgstr ""
+msgstr "Nepodprta naprava '%(device)s', prosim javite hrošča."
#: ../liveusb/creator.py:784 ../liveusb/creator.py:936
#, python-format
msgid "Unsupported filesystem: %s"
-msgstr ""
+msgstr "Nepodprt datotečni sistem: %s"
#: ../liveusb/creator.py:782
#, python-format
msgid ""
"Unsupported filesystem: %s\n"
"In case you are trying to upgrade a manually installed Tails system (that is, if it was installed without this installer), this option is not supported: you need to install it anew to start with, e.g. by choosing the \"Clone & Install\" action instead."
-msgstr ""
+msgstr "Nepodprt datotečni sistem: %s\nV slučaju, da poskušate ročno namestiti sledilni sistem ( to je, če ni bil nameščen s tem zaganjalnikom ), ta opcija ni podprta: namestiti ga morate ponovno z npr.: izbiro \"Clone & Install\" dejanjem."
#: ../liveusb/creator.py:1249
#, python-format
msgid ""
"Unsupported filesystem: %s\n"
"Please backup and format your USB key with the FAT filesystem."
-msgstr ""
+msgstr "Nepodprt datotečni sistem: %s\nProsim formatirajte USB z FAT datotečnim sistemom."
#: ../liveusb/creator.py:882
#, python-format
msgid "Updating properties of system partition %(system_partition)s"
-msgstr ""
+msgstr "Posodabljanje lastnosti sistemske particije %(system_partition)s"
#: ../liveusb/launcher_ui.py:154
msgid "Upgrade from ISO"
-msgstr ""
+msgstr "Posodabljanje iz ISO"
#: ../liveusb/dialog.py:152
msgid "Use existing Live system ISO"
-msgstr ""
+msgstr "Uporabite obstoječ živ sistem ISO"
#: ../liveusb/creator.py:135
msgid "Verifying ISO MD5 checksum"
-msgstr ""
+msgstr "Preverjanje ISO MD5 "
#: ../liveusb/creator.py:364
msgid "Verifying SHA1 checksum of LiveCD image..."
[View Less]
1
0

[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit a9f1d779d4f53e1078d44f1cc909c4393863cebd
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 08:45:28 2014 +0000
Update translations for liveusb-creator
---
sl_SI/sl_SI.po | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index 508bba4..a11f9bc 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-…
[View More]To: \n"
"POT-Creation-Date: 2014-10-15 16:05+0200\n"
-"PO-Revision-Date: 2014-10-19 08:14+0000\n"
+"PO-Revision-Date: 2014-10-19 08:44+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -366,34 +366,34 @@ msgstr "To je USB ključek na katerega želite namestiti živ sistem. Naprava mo
msgid ""
"This is the progress bar that will indicate how far along in the LiveUSB "
"creation process you are"
-msgstr ""
+msgstr "To je vrstica napredka, ki kaže kako daleč ste v procesu ustvarjanja LiveUSB "
#: ../liveusb/dialog.py:162
msgid "This is the status console, where all messages get written to."
-msgstr ""
+msgstr "To je konzola stanja, kjer se zapisujejo vsa sporočila."
#: ../liveusb/creator.py:922
msgid "Trying to continue anyway."
-msgstr ""
+msgstr "Poskušam vseeno nadaljevati."
#: ../liveusb/creator.py:954
#, python-format
msgid "Unable to change volume label: %(message)s"
-msgstr ""
+msgstr "Nemogoče spremeniti nalepko nosilca: %(message)s"
#: ../liveusb/creator.py:492 ../liveusb/creator.py:503
#, python-format
msgid "Unable to chmod %(file)s: %(message)s"
-msgstr ""
+msgstr "Nemogoče v chmod %(file)s: %(message)s"
#: ../liveusb/creator.py:469
#, python-format
msgid "Unable to copy %(infile)s to %(outfile)s: %(message)s"
-msgstr ""
+msgstr "Nemogoče kopirati %(infile)s v %(outfile)s: %(message)s"
#: ../liveusb/gui.py:402
msgid "Unable to find any USB drive"
-msgstr ""
+msgstr "Ne najde nobenega USB pogona"
#: ../liveusb/creator.py:1236
msgid "Unable to find any supported device"
[View Less]
1
0

[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit 69cf0f8be3c1e5767086138adddfb9c94c83c2c5
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 08:15:50 2014 +0000
Update translations for liveusb-creator
---
sl_SI/sl_SI.po | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index 65a00b7..508bba4 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: \n"
"…
[View More]POT-Creation-Date: 2014-10-15 16:05+0200\n"
-"PO-Revision-Date: 2014-10-19 07:41+0000\n"
+"PO-Revision-Date: 2014-10-19 08:14+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -346,7 +346,7 @@ msgid ""
"This button allows you to browse for an existing Live system ISO that you "
"have previously downloaded. If you do not select one, a release will be "
"downloaded for you automatically."
-msgstr ""
+msgstr "Ta gumb vam omogoča brskanje za obstoječimi živimi sistemi ISO, ki ste jih že prenesli. Če ne boste izbrali katerega, bo izdaja prenesena samodejno."
#: ../liveusb/dialog.py:164
msgid ""
@@ -354,13 +354,13 @@ msgid ""
"optionally downloading a release (if an existing one wasn't selected), "
"extracting the ISO to the USB device, creating the persistent overlay, and "
"installing the bootloader."
-msgstr ""
+msgstr "Ta gumb prične s procesom ustvarjanja LiveUSB. To bo sprožilo prenašanje izdaje (če obstoječa ni bila izbrana ), razpakiranje ISO na USB ključek, ustvarilo trajno prikrivanje in namestilo zaganjalnik."
#: ../liveusb/dialog.py:158
msgid ""
"This is the USB stick that you want to install your Live system on. This "
"device must be formatted with the FAT filesystem."
-msgstr ""
+msgstr "To je USB ključek na katerega želite namestiti živ sistem. Naprava mora biti formatirana z FAT datotečnim sistemom."
#: ../liveusb/dialog.py:163
msgid ""
[View Less]
1
0

[translation/liveusb-creator] Update translations for liveusb-creator
by translation@torproject.org 19 Oct '14
by translation@torproject.org 19 Oct '14
19 Oct '14
commit 2e8a0210d162e3becbee9e818e389b57b7630b8c
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Oct 19 07:45:22 2014 +0000
Update translations for liveusb-creator
---
sl_SI/sl_SI.po | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index c106a73..65a00b7 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"…
[View More]Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-15 16:05+0200\n"
-"PO-Revision-Date: 2014-10-18 09:30+0000\n"
+"PO-Revision-Date: 2014-10-19 07:41+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -268,78 +268,78 @@ msgstr "Delitev naprave %(device)s"
#: ../liveusb/gui.py:605
msgid "Persistent Storage"
-msgstr ""
+msgstr "Trajno shranjevanje"
#: ../liveusb/dialog.py:161
msgid "Persistent Storage (0 MB)"
-msgstr ""
+msgstr "Trajno shranjevanje (0 MB)"
#: ../liveusb/gui.py:667 ../liveusb/gui.py:696
msgid "Please confirm your device selection"
-msgstr ""
+msgstr "Prosim, potrdite izbiro naprave"
#: ../liveusb/gui.py:451
msgid "Refreshing releases..."
-msgstr ""
+msgstr "Osveževanje izdaje..."
#: ../liveusb/gui.py:456
msgid "Releases updated!"
-msgstr ""
+msgstr "Izdaje posodobljene!"
#: ../liveusb/creator.py:966 ../liveusb/creator.py:1293
#, python-format
msgid "Removing %(file)s"
-msgstr ""
+msgstr "Odstranjevanje %(file)s"
#: ../liveusb/creator.py:483
msgid "Removing existing Live OS"
-msgstr ""
+msgstr "Odstranitev obstoječe žive distribucije OS"
#: ../liveusb/creator.py:1148
#, python-format
msgid "Resetting Master Boot Record of %s"
-msgstr ""
+msgstr "Ponastavitev MBR %s"
#: ../liveusb/gui.py:758
msgid "Select Live ISO"
-msgstr ""
+msgstr "Izberite živo distribucijo ISO"
#: ../liveusb/creator.py:184
msgid "Setting up OLPC boot file..."
-msgstr ""
+msgstr "Nastavitev OLPC zagonske datoteke..."
#: ../liveusb/creator.py:730
#, python-format
msgid ""
"Some partitions of the target device %(device)s are mounted. They will be "
"unmounted before starting the installation process."
-msgstr ""
+msgstr "Nekatere particije na ciljni napravi %(device)s so vpete. Potrebno jih je odpeti pred začetkom namestitve. "
#: ../liveusb/creator.py:133
msgid ""
"Source type does not support verification of ISO MD5 checksum, skipping"
-msgstr ""
+msgstr "Tip izvora ne podpira kontrole ISO MD5, preskoči"
#: ../liveusb/creator.py:1182
msgid "Synchronizing data on disk..."
-msgstr ""
+msgstr "Sinhronizacija podatkov na disk"
#: ../liveusb/dialog.py:159
msgid "Target Device"
-msgstr ""
+msgstr "Ciljna naprava"
#: ../liveusb/gui.py:761
msgid ""
"The selected file is unreadable. Please fix its permissions or select "
"another file."
-msgstr ""
+msgstr "Izbrana datoteka je nečitljiva. Popravite dovoljenje ali izberite drugo datoteko."
#: ../liveusb/creator.py:345
#, python-format
msgid ""
"There was a problem executing the following command: `%(command)s`.\n"
"A more detailed error log has been written to '%(filename)s'."
-msgstr ""
+msgstr "Nastal je problem pri izvrševanju ukaza: `%(command)s`.\nZa prikaz podrobnosti je bila ustvarjena log datoteka v '%(filename)s'."
#: ../liveusb/dialog.py:151
msgid ""
[View Less]
1
0

[arm/master] Moving graph statistical information to a Stat class
by atagar@torproject.org 19 Oct '14
by atagar@torproject.org 19 Oct '14
19 Oct '14
commit ea6fdd1afbbbe4ac6bf4fc5fbb762d172d53c711
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Oct 18 18:27:38 2014 -0700
Moving graph statistical information to a Stat class
Simple class to represent the statistical information on the graph. Yes, we had
a previous GraphStat class but that confused the method that we'd use to fetch
data with both a primary and secondary stat. This doubled the amount of code we
needed. Oh, and the new Stat has a …
[View More]slightly nicer implementation.
---
arm/graph_panel.py | 185 +++++++++++++++++++++++++---------------------------
1 file changed, 88 insertions(+), 97 deletions(-)
diff --git a/arm/graph_panel.py b/arm/graph_panel.py
index ca91bb2..fab6603 100644
--- a/arm/graph_panel.py
+++ b/arm/graph_panel.py
@@ -86,6 +86,48 @@ CONFIG = conf.config_dict('arm', {
COLLAPSE_WIDTH = 135
+class Stat(object):
+ """
+ Graphable statistical information.
+
+ :var int latest_value: last value we recorded
+ :var int total: sum of all values we've recorded
+ :var dict values: mapping of intervals to an array of samplings from newest to oldest
+ :var dict max_value: mapping of intervals to the maximum value it has had
+ """
+
+ def __init__(self, clone = None):
+ if clone:
+ self.latest_value = clone.latest_value
+ self.total = clone.total
+ self.tick = clone.tick
+ self.values = copy.deepcopy(clone.values)
+ self.max_value = dict(clone.max_value)
+ self._in_process_value = dict(clone._in_process_value)
+ else:
+ self.latest_value = 0
+ self.total = 0
+ self.tick = 0
+ self.values = dict([(i, CONFIG['features.graph.max_width'] * [0]) for i in CONFIG['attr.graph.intervals']])
+ self.max_value = dict([(i, 0) for i in CONFIG['attr.graph.intervals']])
+ self._in_process_value = dict([(i, 0) for i in CONFIG['attr.graph.intervals']])
+
+ def update(self, new_value):
+ self.latest_value = new_value
+ self.total += new_value
+ self.tick += 1
+
+ for interval in CONFIG['attr.graph.intervals']:
+ interval_seconds = int(CONFIG['attr.graph.intervals'][interval])
+ self._in_process_value[interval] += new_value
+
+ if self.tick % interval_seconds == 0:
+ new_entry = self._in_process_value[interval] / interval_seconds
+ self.values[interval] = [new_entry] + self.values[interval][:-1]
+ self.max_value[interval] = max(self.max_value[interval], new_entry)
+ self._in_process_value[interval] = 0
+
+
class GraphStats:
"""
Module that's expected to update dynamically and provide attributes to be
@@ -104,29 +146,8 @@ class GraphStats:
self.is_selected = False
self.is_pause_buffer = False
- # tracked stats
-
- self.tick = 0 # number of processed events
- self.last_primary, self.last_secondary = 0, 0 # most recent registered stats
- self.primary_total, self.secondary_total = 0, 0 # sum of all stats seen
-
- # timescale dependent stats
-
- self.max_column = CONFIG['features.graph.max_width']
- self.max_primary, self.max_secondary = {}, {}
- self.primary_counts, self.secondary_counts = {}, {}
-
- for i in range(len(CONFIG['attr.graph.intervals'])):
- # recent rates for graph
-
- self.max_primary[i] = 0
- self.max_secondary[i] = 0
-
- # historic stats for graph, first is accumulator
- # iterative insert needed to avoid making shallow copies (nasty, nasty gotcha)
-
- self.primary_counts[i] = (self.max_column + 1) * [0]
- self.secondary_counts[i] = (self.max_column + 1) * [0]
+ self.primary = Stat()
+ self.secondary = Stat()
# tracks BW events
@@ -143,15 +164,9 @@ class GraphStats:
if not new_copy:
new_copy = GraphStats()
- new_copy.tick = self.tick
- new_copy.last_primary = self.last_primary
- new_copy.last_secondary = self.last_secondary
- new_copy.primary_total = self.primary_total
- new_copy.secondary_total = self.secondary_total
- new_copy.max_primary = dict(self.max_primary)
- new_copy.max_secondary = dict(self.max_secondary)
- new_copy.primary_counts = copy.deepcopy(self.primary_counts)
- new_copy.secondary_counts = copy.deepcopy(self.secondary_counts)
+ new_copy.primary = Stat(self.primary)
+ new_copy.secondary = Stat(self.secondary)
+
new_copy.is_pause_buffer = True
return new_copy
@@ -172,7 +187,7 @@ class GraphStats:
if self._graph_panel and self.is_selected and not self._graph_panel.is_paused():
# use the minimum of the current refresh rate and the panel's
update_rate = int(CONFIG['attr.graph.intervals'].values()[self._graph_panel.update_interval])
- return (self.tick + 1) % update_rate == 0
+ return (self.primary.tick + 1) % update_rate == 0
else:
return False
@@ -200,31 +215,8 @@ class GraphStats:
is_redraw = self.is_next_tick_redraw()
- self.last_primary, self.last_secondary = primary, secondary
- self.primary_total += primary
- self.secondary_total += secondary
-
- # updates for all time intervals
-
- self.tick += 1
-
- for i in range(len(CONFIG['attr.graph.intervals'])):
- lable, timescale = CONFIG['attr.graph.intervals'].items()[i]
- timescale = int(timescale)
-
- self.primary_counts[i][0] += primary
- self.secondary_counts[i][0] += secondary
-
- if self.tick % timescale == 0:
- self.max_primary[i] = max(self.max_primary[i], self.primary_counts[i][0] / timescale)
- self.primary_counts[i][0] /= timescale
- self.primary_counts[i].insert(0, 0)
- del self.primary_counts[i][self.max_column + 1:]
-
- self.max_secondary[i] = max(self.max_secondary[i], self.secondary_counts[i][0] / timescale)
- self.secondary_counts[i][0] /= timescale
- self.secondary_counts[i].insert(0, 0)
- del self.secondary_counts[i][self.max_column + 1:]
+ self.primary.update(primary)
+ self.secondary.update(secondary)
if is_redraw and self._graph_panel:
self._graph_panel.redraw(True)
@@ -261,8 +253,8 @@ class BandwidthStats(GraphStats):
start_time = system.start_time(controller.get_pid(None))
if read_total and write_total and start_time:
- self.primary_total = int(read_total) / 1024 # Bytes -> KB
- self.secondary_total = int(write_total) / 1024 # Bytes -> KB
+ self.primary.total = int(read_total) / 1024 # Bytes -> KB
+ self.secondary.total = int(write_total) / 1024 # Bytes -> KB
self.start_time = start_time
else:
self.start_time = time.time()
@@ -297,7 +289,7 @@ class BandwidthStats(GraphStats):
# crops starting entries so they're the same size
- entry_count = min(len(bw_read_entries), len(bw_write_entries), self.max_column)
+ entry_count = min(len(bw_read_entries), len(bw_write_entries), CONFIG['features.graph.max_width'])
bw_read_entries = bw_read_entries[len(bw_read_entries) - entry_count:]
bw_write_entries = bw_write_entries[len(bw_write_entries) - entry_count:]
@@ -316,16 +308,13 @@ class BandwidthStats(GraphStats):
for i in range(entry_count):
read_value, write_value = bw_read_entries[i], bw_write_entries[i]
- self.last_primary, self.last_secondary = read_value, write_value
-
- self.primary_counts[interval_index].insert(0, read_value)
- self.secondary_counts[interval_index].insert(0, write_value)
+ self.primary.latest_value, self.secondary.latest_value = read_value / 900, write_value / 900
- self.max_primary[interval_index] = max(self.primary_counts)
- self.max_secondary[interval_index] = max(self.secondary_counts)
+ self.primary.values['15 minute'] = [read_value] + self.primary.values['15 minute'][:-1]
+ self.secondary.values['15 minute'] = [write_value] + self.secondary.values['15 minute'][:-1]
- del self.primary_counts[interval_index][self.max_column + 1:]
- del self.secondary_counts[interval_index][self.max_column + 1:]
+ self.primary.max_value['15 minute'] = max(self.primary.values)
+ self.secondary.max_value['15 minute'] = max(self.secondary.values)
return time.time() - min(stats.last_read_time, stats.last_write_time)
@@ -339,14 +328,14 @@ class BandwidthStats(GraphStats):
return 'Bandwidth (%s):' % stats_label if stats_label else 'Bandwidth:'
def primary_header(self, width):
- stats = ['%-14s' % ('%s/sec' % _size_label(self.last_primary * 1024))]
+ stats = ['%-14s' % ('%s/sec' % _size_label(self.primary.latest_value * 1024))]
# if wide then avg and total are part of the header, otherwise they're on
# the x-axis
if width * 2 > COLLAPSE_WIDTH:
- stats.append('- avg: %s/sec' % _size_label(self.primary_total / (time.time() - self.start_time) * 1024))
- stats.append(', total: %s' % _size_label(self.primary_total * 1024))
+ stats.append('- avg: %s/sec' % _size_label(self.primary.total / (time.time() - self.start_time) * 1024))
+ stats.append(', total: %s' % _size_label(self.primary.total * 1024))
stats_label = str_tools.join(stats, '', width - 12)
@@ -356,14 +345,14 @@ class BandwidthStats(GraphStats):
return 'Download:'
def secondary_header(self, width):
- stats = ['%-14s' % ('%s/sec' % _size_label(self.last_secondary * 1024))]
+ stats = ['%-14s' % ('%s/sec' % _size_label(self.secondary.latest_value * 1024))]
# if wide then avg and total are part of the header, otherwise they're on
# the x-axis
if width * 2 > COLLAPSE_WIDTH:
- stats.append('- avg: %s/sec' % _size_label(self.secondary_total / (time.time() - self.start_time) * 1024))
- stats.append(', total: %s' % _size_label(self.secondary_total * 1024))
+ stats.append('- avg: %s/sec' % _size_label(self.secondary.total / (time.time() - self.start_time) * 1024))
+ stats.append(', total: %s' % _size_label(self.secondary.total * 1024))
stats_label = str_tools.join(stats, '', width - 10)
@@ -455,12 +444,12 @@ class ConnStats(GraphStats):
return 'Connection Count:'
def primary_header(self, width):
- avg = self.primary_total / max(1, self.tick)
- return 'Inbound (%s, avg: %s):' % (self.last_primary, avg)
+ avg = self.primary.total / max(1, self.primary.tick)
+ return 'Inbound (%s, avg: %s):' % (self.primary.latest_value, avg)
def secondary_header(self, width):
- avg = self.secondary_total / max(1, self.tick)
- return 'Outbound (%s, avg: %s):' % (self.last_secondary, avg)
+ avg = self.secondary.total / max(1, self.secondary.tick)
+ return 'Outbound (%s, avg: %s):' % (self.secondary.latest_value, avg)
class ResourceStats(GraphStats):
@@ -482,15 +471,15 @@ class ResourceStats(GraphStats):
return 'System Resources:'
def primary_header(self, width):
- avg = self.primary_total / max(1, self.tick)
- return 'CPU (%0.1f%%, avg: %0.1f%%):' % (self.last_primary, avg)
+ avg = self.primary.total / max(1, self.primary.tick)
+ return 'CPU (%0.1f%%, avg: %0.1f%%):' % (self.primary.latest_value, avg)
def secondary_header(self, width):
# memory sizes are converted from MB to B before generating labels
- usage_label = str_tools.size_label(self.last_secondary * 1048576, 1)
+ usage_label = str_tools.size_label(self.secondary.latest_value * 1048576, 1)
- avg = self.secondary_total / max(1, self.tick)
+ avg = self.secondary.total / max(1, self.secondary.tick)
avg_label = str_tools.size_label(avg * 1048576, 1)
return 'Memory (%s, avg: %s):' % (usage_label, avg_label)
@@ -741,7 +730,7 @@ class GraphPanel(panel.Panel):
return
param = self.get_attr('stats')[self.current_display]
- graph_column = min((width - 10) / 2, param.max_column)
+ graph_column = min((width - 10) / 2, CONFIG['features.graph.max_width'])
if self.is_title_visible():
self.addstr(0, 0, param.get_title(width), curses.A_STANDOUT)
@@ -758,23 +747,25 @@ class GraphPanel(panel.Panel):
# determines max/min value on the graph
+ interval = CONFIG['attr.graph.intervals'].keys()[self.update_interval]
+
if self.bounds == Bounds.GLOBAL_MAX:
- primary_max_bound = int(param.max_primary[self.update_interval])
- secondary_max_bound = int(param.max_secondary[self.update_interval])
+ primary_max_bound = param.primary.max_value[interval]
+ secondary_max_bound = param.secondary.max_value[interval]
else:
# both Bounds.LOCAL_MAX and Bounds.TIGHT use local maxima
if graph_column < 2:
# nothing being displayed
primary_max_bound, secondary_max_bound = 0, 0
else:
- primary_max_bound = int(max(param.primary_counts[self.update_interval][1:graph_column + 1]))
- secondary_max_bound = int(max(param.secondary_counts[self.update_interval][1:graph_column + 1]))
+ primary_max_bound = max(param.primary.values[interval][:graph_column])
+ secondary_max_bound = max(param.secondary.values[interval][:graph_column])
primary_min_bound = secondary_min_bound = 0
if self.bounds == Bounds.TIGHT:
- primary_min_bound = int(min(param.primary_counts[self.update_interval][1:graph_column + 1]))
- secondary_min_bound = int(min(param.secondary_counts[self.update_interval][1:graph_column + 1]))
+ primary_min_bound = min(param.primary.values[interval][:graph_column])
+ secondary_min_bound = min(param.secondary.values[interval][:graph_column])
# if the max = min (ie, all values are the same) then use zero lower
# bound so a graph is still displayed
@@ -819,14 +810,14 @@ class GraphPanel(panel.Panel):
# creates bar graph (both primary and secondary)
for col in range(graph_column):
- column_count = int(param.primary_counts[self.update_interval][col + 1]) - primary_min_bound
- column_height = min(self.graph_height, self.graph_height * column_count / (max(1, primary_max_bound) - primary_min_bound))
+ column_count = int(param.primary.values[interval][col]) - primary_min_bound
+ column_height = int(min(self.graph_height, self.graph_height * column_count / (max(1, primary_max_bound) - primary_min_bound)))
for row in range(column_height):
self.addstr(self.graph_height + 1 - row, col + 5, ' ', curses.A_STANDOUT, PRIMARY_COLOR)
- column_count = int(param.secondary_counts[self.update_interval][col + 1]) - secondary_min_bound
- column_height = min(self.graph_height, self.graph_height * column_count / (max(1, secondary_max_bound) - secondary_min_bound))
+ column_count = int(param.secondary.values[interval][col]) - secondary_min_bound
+ column_height = int(min(self.graph_height, self.graph_height * column_count / (max(1, secondary_max_bound) - secondary_min_bound)))
for row in range(column_height):
self.addstr(self.graph_height + 1 - row, col + graph_column + 10, ' ', curses.A_STANDOUT, SECONDARY_COLOR)
@@ -863,11 +854,11 @@ class GraphPanel(panel.Panel):
# clears line
self.addstr(labeling_line, 0, ' ' * width)
- graph_column = min((width - 10) / 2, param.max_column)
+ graph_column = min((width - 10) / 2, CONFIG['features.graph.max_width'])
runtime = time.time() - param.start_time
- primary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.primary_total * 1024), _size_label(param.primary_total / runtime * 1024))
- secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.secondary_total * 1024), _size_label(param.secondary_total / runtime * 1024))
+ primary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.primary.total * 1024), _size_label(param.primary.total / runtime * 1024))
+ secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.secondary.total * 1024), _size_label(param.secondary.total / runtime * 1024))
self.addstr(labeling_line, 1, primary_footer, PRIMARY_COLOR)
self.addstr(labeling_line, graph_column + 6, secondary_footer, SECONDARY_COLOR)
[View Less]
1
0

[arm/master] Moving x-axis overriding from bandwidth stats to panel
by atagar@torproject.org 19 Oct '14
by atagar@torproject.org 19 Oct '14
19 Oct '14
commit ca2cce49db1bfff9c36408f5b14efd8f65cfe89c
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Oct 18 14:34:33 2014 -0700
Moving x-axis overriding from bandwidth stats to panel
This lets us drop custom draw handling from the stats class.
---
arm/graph_panel.py | 45 ++++++++++++++++-----------------------------
1 file changed, 16 insertions(+), 29 deletions(-)
diff --git a/arm/graph_panel.py b/arm/graph_panel.py
index 7072f38..ca91bb2 100644
--- a/arm/…
[View More]graph_panel.py
+++ b/arm/graph_panel.py
@@ -189,13 +189,6 @@ class GraphStats:
def secondary_header(self, width):
return ''
- def draw(self, panel, width, height):
- """
- Allows for any custom drawing monitor wishes to append.
- """
-
- pass
-
def bandwidth_event(self, event):
if not self.is_pause_buffer:
self.event_tick()
@@ -341,26 +334,6 @@ class BandwidthStats(GraphStats):
self._process_event(event.read / 1024.0, event.written / 1024.0)
- def draw(self, panel, width, height):
- # line of the graph's x-axis labeling
-
- labeling_line = DEFAULT_CONTENT_HEIGHT + panel.graph_height - 2
-
- # if display is narrow, overwrites x-axis labels with avg / total stats
-
- if width <= COLLAPSE_WIDTH:
- # clears line
-
- panel.addstr(labeling_line, 0, ' ' * width)
- graph_column = min((width - 10) / 2, self.max_column)
-
- runtime = time.time() - self.start_time
- primary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.primary_total * 1024), _size_label(self.primary_total / runtime * 1024))
- secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.secondary_total * 1024), _size_label(self.secondary_total / runtime * 1024))
-
- panel.addstr(labeling_line, 1, primary_footer, PRIMARY_COLOR)
- panel.addstr(labeling_line, graph_column + 6, secondary_footer, SECONDARY_COLOR)
-
def get_title(self, width):
stats_label = str_tools.join(self._title_stats, ', ', width - 13)
return 'Bandwidth (%s):' % stats_label if stats_label else 'Bandwidth:'
@@ -882,7 +855,22 @@ class GraphPanel(panel.Panel):
self.addstr(self.graph_height + 2, 4 + loc, time_label, PRIMARY_COLOR)
self.addstr(self.graph_height + 2, graph_column + 10 + loc, time_label, SECONDARY_COLOR)
- param.draw(self, width, height) # allows current stats to modify the display
+ # if display is narrow, overwrites x-axis labels with avg / total stats
+
+ labeling_line = DEFAULT_CONTENT_HEIGHT + self.graph_height - 2
+
+ if self.current_display == GraphStat.BANDWIDTH and width <= COLLAPSE_WIDTH:
+ # clears line
+
+ self.addstr(labeling_line, 0, ' ' * width)
+ graph_column = min((width - 10) / 2, param.max_column)
+
+ runtime = time.time() - param.start_time
+ primary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.primary_total * 1024), _size_label(param.primary_total / runtime * 1024))
+ secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(param.secondary_total * 1024), _size_label(param.secondary_total / runtime * 1024))
+
+ self.addstr(labeling_line, 1, primary_footer, PRIMARY_COLOR)
+ self.addstr(labeling_line, graph_column + 6, secondary_footer, SECONDARY_COLOR)
# provides accounting stats if enabled
@@ -892,7 +880,6 @@ class GraphPanel(panel.Panel):
if tor_controller().is_alive():
hibernate_color = CONFIG['attr.hibernate_color'].get(accounting_stats.status, 'red')
- labeling_line = DEFAULT_CONTENT_HEIGHT + self.graph_height - 2
x, y = 0, labeling_line + 2
x = self.addstr(y, x, 'Accounting (', curses.A_BOLD)
x = self.addstr(y, x, accounting_stats.status, curses.A_BOLD, hibernate_color)
[View Less]
1
0

19 Oct '14
commit 3907aa3dcda05b073248e787592b62e68bef1b97
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Oct 12 16:42:58 2014 -0700
Moving UPDATE_INTERVALS to configuration
Simply moving a constant from the graph panel to the config.
---
arm/config/attributes.cfg | 9 ++++++++
arm/graphing/bandwidth_stats.py | 5 +++--
arm/graphing/graph_panel.py | 45 ++++++++++++++-------------------------
arm/menu/actions.py | 11 ++++++----
4 files …
[View More]changed, 35 insertions(+), 35 deletions(-)
diff --git a/arm/config/attributes.cfg b/arm/config/attributes.cfg
index 6f1fea5..5b49a37 100644
--- a/arm/config/attributes.cfg
+++ b/arm/config/attributes.cfg
@@ -26,3 +26,12 @@ attr.version_status_colors unknown => cyan
attr.hibernate_color awake => green
attr.hibernate_color soft => yellow
attr.hibernate_color hard => red
+
+attr.graph.intervals each second => 1
+attr.graph.intervals 5 seconds => 5
+attr.graph.intervals 30 seconds => 30
+attr.graph.intervals minutely => 60
+attr.graph.intervals 15 minute => 900
+attr.graph.intervals 30 minute => 1800
+attr.graph.intervals hourly => 3600
+attr.graph.intervals daily => 86400
diff --git a/arm/graphing/bandwidth_stats.py b/arm/graphing/bandwidth_stats.py
index f91ce98..4d52aae 100644
--- a/arm/graphing/bandwidth_stats.py
+++ b/arm/graphing/bandwidth_stats.py
@@ -18,6 +18,7 @@ ACCOUNTING_RATE = 5
CONFIG = conf.config_dict('arm', {
'attr.hibernate_color': {},
+ 'attr.graph.intervals': {},
'features.graph.bw.transferInBytes': False,
'features.graph.bw.accounting.show': True,
'tor.chroot': '',
@@ -124,8 +125,8 @@ class BandwidthStats(graph_panel.GraphStats):
interval_index = 0
- for index_entry in graph_panel.UPDATE_INTERVALS:
- if index_entry[1] == 900:
+ for interval_rate in CONFIG['attr.graph.intervals'].values():
+ if int(interval_rate) == 900:
break
else:
interval_index += 1
diff --git a/arm/graphing/graph_panel.py b/arm/graphing/graph_panel.py
index 99c1d3b..77f9404 100644
--- a/arm/graphing/graph_panel.py
+++ b/arm/graphing/graph_panel.py
@@ -28,20 +28,7 @@ from arm.util import msg, panel, tor_controller
from stem.util import conf, enum, log, str_tools
-# time intervals at which graphs can be updated
-
-UPDATE_INTERVALS = [
- ('each second', 1),
- ('5 seconds', 5),
- ('30 seconds', 30),
- ('minutely', 60),
- ('15 minute', 900),
- ('30 minute', 1800),
- ('hourly', 3600),
- ('daily', 86400),
-]
-
-GraphStat = enum.Enum("BANDWIDTH", "CONNECTIONS", "SYSTEM_RESOURCES")
+GraphStat = enum.Enum('BANDWIDTH', 'CONNECTIONS', 'SYSTEM_RESOURCES')
# maps 'features.graph.type' config values to the initial types
@@ -66,8 +53,6 @@ def conf_handler(key, value):
return max(MIN_GRAPH_HEIGHT, value)
elif key == 'features.graph.max_width':
return max(1, value)
- elif key == 'features.graph.interval':
- return max(0, min(len(UPDATE_INTERVALS) - 1, value))
elif key == 'features.graph.bound':
return max(0, min(2, value))
@@ -75,6 +60,7 @@ def conf_handler(key, value):
# used for setting defaults when initializing GraphStats and GraphPanel instances
CONFIG = conf.config_dict('arm', {
+ 'attr.graph.intervals': {},
'features.graph.height': 7,
'features.graph.interval': 0,
'features.graph.bound': 1,
@@ -90,7 +76,7 @@ class GraphStats:
"""
Module that's expected to update dynamically and provide attributes to be
graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
- time and timescale parameters use the labels defined in UPDATE_INTERVALS.
+ time and timescale parameters use the labels defined in CONFIG['attr.graph.intervals'].
"""
def __init__(self):
@@ -116,7 +102,7 @@ class GraphStats:
self.max_primary, self.max_secondary = {}, {}
self.primary_counts, self.secondary_counts = {}, {}
- for i in range(len(UPDATE_INTERVALS)):
+ for i in range(len(CONFIG['attr.graph.intervals'])):
# recent rates for graph
self.max_primary[i] = 0
@@ -171,7 +157,7 @@ class GraphStats:
if self._graph_panel and self.is_selected and not self._graph_panel.is_paused():
# use the minimum of the current refresh rate and the panel's
- update_rate = UPDATE_INTERVALS[self._graph_panel.update_interval][1]
+ update_rate = int(CONFIG['attr.graph.intervals'].values()[self._graph_panel.update_interval])
return (self.tick + 1) % update_rate == 0
else:
return False
@@ -222,8 +208,9 @@ class GraphStats:
self.tick += 1
- for i in range(len(UPDATE_INTERVALS)):
- lable, timescale = UPDATE_INTERVALS[i]
+ for i in range(len(CONFIG['attr.graph.intervals'])):
+ lable, timescale = CONFIG['attr.graph.intervals'].items()[i]
+ timescale = int(timescale)
self.primary_counts[i][0] += primary
self.secondary_counts[i][0] += secondary
@@ -252,6 +239,10 @@ class GraphPanel(panel.Panel):
def __init__(self, stdscr):
panel.Panel.__init__(self, stdscr, 'graph', 0)
self.update_interval = CONFIG['features.graph.interval']
+
+ if self.update_interval < 0 or self.update_interval > len(CONFIG['attr.graph.intervals']) - 1:
+ self.update_interval = 0 # user configured it with a value that's out of bounds
+
self.bounds = list(Bounds)[CONFIG['features.graph.bound']]
self.graph_height = CONFIG['features.graph.height']
self.current_display = None # label of the stats currently being displayed
@@ -286,7 +277,7 @@ class GraphPanel(panel.Panel):
else:
log.notice(msg('panel.graphing.prepopulation_all_successful'))
- self.update_interval = 4
+ self.update_interval = 4
except ValueError as exc:
log.info(msg('panel.graphing.prepopulation_failure', error = str(exc)))
@@ -419,7 +410,7 @@ class GraphPanel(panel.Panel):
elif key.match('i'):
# provides menu to pick graph panel update interval
- options = [label for (label, _) in UPDATE_INTERVALS]
+ options = CONFIG['attr.graph.intervals'].keys()
selection = arm.popups.show_menu('Update Interval:', options, self.update_interval)
if selection != -1:
@@ -434,7 +425,7 @@ class GraphPanel(panel.Panel):
('r', 'resize graph', None),
('s', 'graphed stats', self.current_display if self.current_display else 'none'),
('b', 'graph bounds', self.bounds.lower()),
- ('i', 'graph update interval', UPDATE_INTERVALS[self.update_interval][0]),
+ ('i', 'graph update interval', CONFIG['attr.graph.intervals'].keys()[self.update_interval]),
]
def draw(self, width, height):
@@ -534,11 +525,7 @@ class GraphPanel(panel.Panel):
# bottom labeling of x-axis
- interval_sec = 1 # seconds per labeling
-
- for i in range(len(UPDATE_INTERVALS)):
- if i == self.update_interval:
- interval_sec = UPDATE_INTERVALS[i][1]
+ interval_sec = int(CONFIG['attr.graph.intervals'].values()[self.update_interval]) # seconds per labeling
interval_spacing = 10 if graph_column >= WIDE_LABELING_GRAPH_COL else 5
units_label, decimal_precision = None, 0
diff --git a/arm/menu/actions.py b/arm/menu/actions.py
index 3aa941d..b95f1fc 100644
--- a/arm/menu/actions.py
+++ b/arm/menu/actions.py
@@ -17,8 +17,9 @@ import stem.util.connection
from stem.util import conf, str_tools
-CONFIG = conf.config_dict("arm", {
- "features.log.showDuplicateEntries": False,
+CONFIG = conf.config_dict('arm', {
+ 'features.log.showDuplicateEntries': False,
+ 'attr.graph.intervals': {},
})
@@ -167,8 +168,10 @@ def make_graph_menu(graph_panel):
interval_menu = arm.menu.item.Submenu("Interval")
interval_group = arm.menu.item.SelectionGroup(graph_panel.set_update_interval, graph_panel.get_update_interval())
- for i in range(len(arm.graphing.graph_panel.UPDATE_INTERVALS)):
- label = arm.graphing.graph_panel.UPDATE_INTERVALS[i][0]
+ graph_intervals = CONFIG['attr.graph.intervals']
+
+ for i in range(len(graph_intervals)):
+ label = graph_intervals.keys()[i]
label = str_tools._to_camel_case(label, divider = " ")
interval_menu.add(arm.menu.item.SelectionMenuItem(label, interval_group, i))
[View Less]
1
0
commit f3d55aa694bffdbc1ce95046fea4d01e7e1917b1
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Oct 12 19:43:17 2014 -0700
Merging graph module into single file
Devious scheme is to rip this module a new one, simplifying it to the point
that it can comfortably live in a single file. It's presently way on the long
end of uncomfortable, but merging it will help push us along with this
overhaul.
---
arm/controller.py | 11 +-
arm/…
[View More]graph_panel.py | 947 +++++++++++++++++++++++++++++++++++++++
arm/graphing/__init__.py | 10 -
arm/graphing/bandwidth_stats.py | 285 ------------
arm/graphing/conn_stats.py | 59 ---
arm/graphing/graph_panel.py | 581 ------------------------
arm/graphing/resource_stats.py | 57 ---
arm/menu/actions.py | 4 +-
8 files changed, 953 insertions(+), 1001 deletions(-)
diff --git a/arm/controller.py b/arm/controller.py
index 42d0531..1525412 100644
--- a/arm/controller.py
+++ b/arm/controller.py
@@ -15,10 +15,7 @@ import arm.header_panel
import arm.log_panel
import arm.config_panel
import arm.torrc_panel
-import arm.graphing.graph_panel
-import arm.graphing.bandwidth_stats
-import arm.graphing.conn_stats
-import arm.graphing.resource_stats
+import arm.graph_panel
import arm.connections.conn_panel
import arm.util.tracker
@@ -26,9 +23,9 @@ import stem
from stem.control import State
-from arm.util import msg, panel, tor_config, tor_controller, ui_tools
+from arm.util import panel, tor_config, tor_controller, ui_tools
-from stem.util import conf, enum, log, str_tools, system
+from stem.util import conf, log, system
ARM_CONTROLLER = None
@@ -105,7 +102,7 @@ def init_controller(stdscr, start_time):
# first page: graph and log
if CONFIG["features.panels.show.graph"]:
- first_page_panels.append(arm.graphing.graph_panel.GraphPanel(stdscr))
+ first_page_panels.append(arm.graph_panel.GraphPanel(stdscr))
if CONFIG["features.panels.show.log"]:
expanded_events = arm.arguments.expand_events(CONFIG["startup.events"])
diff --git a/arm/graph_panel.py b/arm/graph_panel.py
new file mode 100644
index 0000000..8f08fb3
--- /dev/null
+++ b/arm/graph_panel.py
@@ -0,0 +1,947 @@
+"""
+Flexible panel for presenting bar graphs for a variety of stats. This panel is
+just concerned with the rendering of information, which is actually collected
+and stored by implementations of the GraphStats interface. Panels are made up
+of a title, followed by headers and graphs for two sets of stats. For
+instance...
+
+Bandwidth (cap: 5 MB, burst: 10 MB):
+Downloaded (0.0 B/sec): Uploaded (0.0 B/sec):
+ 34 30
+ * *
+ ** * * * **
+ * * * ** ** ** *** ** ** ** **
+ ********* ****** ****** ********* ****** ******
+ 0 ************ **************** 0 ************ ****************
+ 25s 50 1m 1.6 2.0 25s 50 1m 1.6 2.0
+"""
+
+import copy
+import curses
+import time
+
+import arm.popups
+import arm.controller
+import arm.util.tracker
+
+import stem.control
+
+from arm.util import bandwidth_from_state, msg, panel, tor_controller
+
+from stem.control import Listener, State
+from stem.util import conf, enum, log, str_tools, system
+
+GraphStat = enum.Enum('BANDWIDTH', 'CONNECTIONS', 'SYSTEM_RESOURCES')
+
+# maps 'features.graph.type' config values to the initial types
+
+GRAPH_INIT_STATS = {1: GraphStat.BANDWIDTH, 2: GraphStat.CONNECTIONS, 3: GraphStat.SYSTEM_RESOURCES}
+
+DEFAULT_CONTENT_HEIGHT = 4 # space needed for labeling above and below the graph
+PRIMARY_COLOR, SECONDARY_COLOR = 'green', 'cyan'
+MIN_GRAPH_HEIGHT = 1
+
+# enums for graph bounds:
+# Bounds.GLOBAL_MAX - global maximum (highest value ever seen)
+# Bounds.LOCAL_MAX - local maximum (highest value currently on the graph)
+# Bounds.TIGHT - local maximum and minimum
+
+Bounds = enum.Enum('GLOBAL_MAX', 'LOCAL_MAX', 'TIGHT')
+
+WIDE_LABELING_GRAPH_COL = 50 # minimum graph columns to use wide spacing for x-axis labels
+
+ACCOUNTING_RATE = 5
+
+
+def conf_handler(key, value):
+ if key == 'features.graph.height':
+ return max(MIN_GRAPH_HEIGHT, value)
+ elif key == 'features.graph.max_width':
+ return max(1, value)
+ elif key == 'features.graph.bound':
+ return max(0, min(2, value))
+
+
+# used for setting defaults when initializing GraphStats and GraphPanel instances
+
+CONFIG = conf.config_dict('arm', {
+ 'attr.hibernate_color': {},
+ 'attr.graph.intervals': {},
+ 'features.graph.height': 7,
+ 'features.graph.interval': 0,
+ 'features.graph.bound': 1,
+ 'features.graph.max_width': 150,
+ 'features.graph.showIntermediateBounds': True,
+ 'features.graph.type': 1,
+ 'features.panels.show.connection': True,
+ 'features.graph.bw.prepopulate': True,
+ 'features.graph.bw.transferInBytes': False,
+ 'features.graph.bw.accounting.show': True,
+ 'tor.chroot': '',
+}, conf_handler)
+
+# width at which panel abandons placing optional stats (avg and total) with
+# header in favor of replacing the x-axis label
+
+COLLAPSE_WIDTH = 135
+
+
+class GraphStats:
+ """
+ Module that's expected to update dynamically and provide attributes to be
+ graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
+ time and timescale parameters use the labels defined in CONFIG['attr.graph.intervals'].
+ """
+
+ def __init__(self):
+ """
+ Initializes parameters needed to present a graph.
+ """
+
+ # panel to be redrawn when updated (set when added to GraphPanel)
+
+ self._graph_panel = None
+ self.is_selected = False
+ self.is_pause_buffer = False
+
+ # tracked stats
+
+ self.tick = 0 # number of processed events
+ self.last_primary, self.last_secondary = 0, 0 # most recent registered stats
+ self.primary_total, self.secondary_total = 0, 0 # sum of all stats seen
+
+ # timescale dependent stats
+
+ self.max_column = CONFIG['features.graph.max_width']
+ self.max_primary, self.max_secondary = {}, {}
+ self.primary_counts, self.secondary_counts = {}, {}
+
+ for i in range(len(CONFIG['attr.graph.intervals'])):
+ # recent rates for graph
+
+ self.max_primary[i] = 0
+ self.max_secondary[i] = 0
+
+ # historic stats for graph, first is accumulator
+ # iterative insert needed to avoid making shallow copies (nasty, nasty gotcha)
+
+ self.primary_counts[i] = (self.max_column + 1) * [0]
+ self.secondary_counts[i] = (self.max_column + 1) * [0]
+
+ # tracks BW events
+
+ tor_controller().add_event_listener(self.bandwidth_event, stem.control.EventType.BW)
+
+ def clone(self, new_copy=None):
+ """
+ Provides a deep copy of this instance.
+
+ Arguments:
+ new_copy - base instance to build copy off of
+ """
+
+ if not new_copy:
+ new_copy = GraphStats()
+
+ new_copy.tick = self.tick
+ new_copy.last_primary = self.last_primary
+ new_copy.last_secondary = self.last_secondary
+ new_copy.primary_total = self.primary_total
+ new_copy.secondary_total = self.secondary_total
+ new_copy.max_primary = dict(self.max_primary)
+ new_copy.max_secondary = dict(self.max_secondary)
+ new_copy.primary_counts = copy.deepcopy(self.primary_counts)
+ new_copy.secondary_counts = copy.deepcopy(self.secondary_counts)
+ new_copy.is_pause_buffer = True
+ return new_copy
+
+ def event_tick(self):
+ """
+ Called when it's time to process another event. All graphs use tor BW
+ events to keep in sync with each other (this happens once a second).
+ """
+
+ pass
+
+ def is_next_tick_redraw(self):
+ """
+ Provides true if the following tick (call to _process_event) will result in
+ being redrawn.
+ """
+
+ if self._graph_panel and self.is_selected and not self._graph_panel.is_paused():
+ # use the minimum of the current refresh rate and the panel's
+ update_rate = int(CONFIG['attr.graph.intervals'].values()[self._graph_panel.update_interval])
+ return (self.tick + 1) % update_rate == 0
+ else:
+ return False
+
+ def get_title(self, width):
+ """
+ Provides top label.
+ """
+
+ return ''
+
+ def primary_header(self, width):
+ return ''
+
+ def secondary_header(self, width):
+ return ''
+
+ def get_content_height(self):
+ """
+ Provides the height content should take up (not including the graph).
+ """
+
+ return DEFAULT_CONTENT_HEIGHT
+
+ def draw(self, panel, width, height):
+ """
+ Allows for any custom drawing monitor wishes to append.
+ """
+
+ pass
+
+ def bandwidth_event(self, event):
+ if not self.is_pause_buffer:
+ self.event_tick()
+
+ def _process_event(self, primary, secondary):
+ """
+ Includes new stats in graphs and notifies associated GraphPanel of changes.
+ """
+
+ is_redraw = self.is_next_tick_redraw()
+
+ self.last_primary, self.last_secondary = primary, secondary
+ self.primary_total += primary
+ self.secondary_total += secondary
+
+ # updates for all time intervals
+
+ self.tick += 1
+
+ for i in range(len(CONFIG['attr.graph.intervals'])):
+ lable, timescale = CONFIG['attr.graph.intervals'].items()[i]
+ timescale = int(timescale)
+
+ self.primary_counts[i][0] += primary
+ self.secondary_counts[i][0] += secondary
+
+ if self.tick % timescale == 0:
+ self.max_primary[i] = max(self.max_primary[i], self.primary_counts[i][0] / timescale)
+ self.primary_counts[i][0] /= timescale
+ self.primary_counts[i].insert(0, 0)
+ del self.primary_counts[i][self.max_column + 1:]
+
+ self.max_secondary[i] = max(self.max_secondary[i], self.secondary_counts[i][0] / timescale)
+ self.secondary_counts[i][0] /= timescale
+ self.secondary_counts[i].insert(0, 0)
+ del self.secondary_counts[i][self.max_column + 1:]
+
+ if is_redraw and self._graph_panel:
+ self._graph_panel.redraw(True)
+
+
+class BandwidthStats(GraphStats):
+ """
+ Uses tor BW events to generate bandwidth usage graph.
+ """
+
+ def __init__(self, is_pause_buffer = False):
+ GraphStats.__init__(self)
+
+ # listens for tor reload (sighup) events which can reset the bandwidth
+ # rate/burst and if tor's using accounting
+
+ controller = tor_controller()
+ self._title_stats = []
+ self._accounting_stats = None
+
+ if not is_pause_buffer:
+ self.reset_listener(controller, State.INIT, None) # initializes values
+
+ controller.add_status_listener(self.reset_listener)
+ self.new_desc_event(None) # updates title params
+
+ # We both show our 'total' attributes and use it to determine our average.
+ #
+ # If we can get *both* our start time and the totals from tor (via 'GETINFO
+ # traffic/*') then that's ideal, but if not then just track the total for
+ # the time arm is run.
+
+ read_total = controller.get_info('traffic/read', None)
+ write_total = controller.get_info('traffic/written', None)
+ start_time = system.start_time(controller.get_pid(None))
+
+ if read_total and write_total and start_time:
+ self.primary_total = int(read_total) / 1024 # Bytes -> KB
+ self.secondary_total = int(write_total) / 1024 # Bytes -> KB
+ self.start_time = start_time
+ else:
+ self.start_time = time.time()
+
+ def clone(self, new_copy = None):
+ if not new_copy:
+ new_copy = BandwidthStats(True)
+
+ new_copy._accounting_stats = self._accounting_stats
+ new_copy._title_stats = self._title_stats
+
+ return GraphStats.clone(self, new_copy)
+
+ def reset_listener(self, controller, event_type, _):
+ # updates title parameters and accounting status if they changed
+
+ self.new_desc_event(None) # updates title params
+
+ if event_type in (State.INIT, State.RESET) and CONFIG['features.graph.bw.accounting.show']:
+ is_accounting_enabled = controller.get_info('accounting/enabled', None) == '1'
+
+ if is_accounting_enabled != bool(self._accounting_stats):
+ self._accounting_stats = tor_controller().get_accounting_stats(None)
+
+ # redraws the whole screen since our height changed
+
+ arm.controller.get_controller().redraw()
+
+ # redraws to reflect changes (this especially noticeable when we have
+ # accounting and shut down since it then gives notice of the shutdown)
+
+ if self._graph_panel and self.is_selected:
+ self._graph_panel.redraw(True)
+
+ def prepopulate_from_state(self):
+ """
+ Attempts to use tor's state file to prepopulate values for the 15 minute
+ interval via the BWHistoryReadValues/BWHistoryWriteValues values. This
+ returns True if successful and False otherwise.
+ """
+
+ stats = bandwidth_from_state()
+
+ missing_read_entries = int((time.time() - stats.last_read_time) / 900)
+ missing_write_entries = int((time.time() - stats.last_write_time) / 900)
+
+ # fills missing entries with the last value
+
+ bw_read_entries = stats.read_entries + [stats.read_entries[-1]] * missing_read_entries
+ bw_write_entries = stats.write_entries + [stats.write_entries[-1]] * missing_write_entries
+
+ # crops starting entries so they're the same size
+
+ entry_count = min(len(bw_read_entries), len(bw_write_entries), self.max_column)
+ bw_read_entries = bw_read_entries[len(bw_read_entries) - entry_count:]
+ bw_write_entries = bw_write_entries[len(bw_write_entries) - entry_count:]
+
+ # gets index for 15-minute interval
+
+ interval_index = 0
+
+ for interval_rate in CONFIG['attr.graph.intervals'].values():
+ if int(interval_rate) == 900:
+ break
+ else:
+ interval_index += 1
+
+ # fills the graphing parameters with state information
+
+ for i in range(entry_count):
+ read_value, write_value = bw_read_entries[i], bw_write_entries[i]
+
+ self.last_primary, self.last_secondary = read_value, write_value
+
+ self.primary_counts[interval_index].insert(0, read_value)
+ self.secondary_counts[interval_index].insert(0, write_value)
+
+ self.max_primary[interval_index] = max(self.primary_counts)
+ self.max_secondary[interval_index] = max(self.secondary_counts)
+
+ del self.primary_counts[interval_index][self.max_column + 1:]
+ del self.secondary_counts[interval_index][self.max_column + 1:]
+
+ return time.time() - min(stats.last_read_time, stats.last_write_time)
+
+ def bandwidth_event(self, event):
+ if self._accounting_stats and self.is_next_tick_redraw():
+ if time.time() - self._accounting_stats.retrieved >= ACCOUNTING_RATE:
+ self._accounting_stats = tor_controller().get_accounting_stats(None)
+
+ # scales units from B to KB for graphing
+
+ self._process_event(event.read / 1024.0, event.written / 1024.0)
+
+ def draw(self, panel, width, height):
+ # line of the graph's x-axis labeling
+
+ labeling_line = GraphStats.get_content_height(self) + panel.graph_height - 2
+
+ # if display is narrow, overwrites x-axis labels with avg / total stats
+
+ if width <= COLLAPSE_WIDTH:
+ # clears line
+
+ panel.addstr(labeling_line, 0, ' ' * width)
+ graph_column = min((width - 10) / 2, self.max_column)
+
+ runtime = time.time() - self.start_time
+ primary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.primary_total * 1024), _size_label(self.primary_total / runtime * 1024))
+ secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.secondary_total * 1024), _size_label(self.secondary_total / runtime * 1024))
+
+ panel.addstr(labeling_line, 1, primary_footer, PRIMARY_COLOR)
+ panel.addstr(labeling_line, graph_column + 6, secondary_footer, SECONDARY_COLOR)
+
+ # provides accounting stats if enabled
+
+ if self._accounting_stats:
+ if tor_controller().is_alive():
+ hibernate_color = CONFIG['attr.hibernate_color'].get(self._accounting_stats.status, 'red')
+
+ x, y = 0, labeling_line + 2
+ x = panel.addstr(y, x, 'Accounting (', curses.A_BOLD)
+ x = panel.addstr(y, x, self._accounting_stats.status, curses.A_BOLD, hibernate_color)
+ x = panel.addstr(y, x, ')', curses.A_BOLD)
+
+ panel.addstr(y, 35, 'Time to reset: %s' % str_tools.short_time_label(self._accounting_stats.time_until_reset))
+
+ panel.addstr(y + 1, 2, '%s / %s' % (self._accounting_stats.read_bytes, self._accounting_stats.read_limit), PRIMARY_COLOR)
+ panel.addstr(y + 1, 37, '%s / %s' % (self._accounting_stats.written_bytes, self._accounting_stats.write_limit), SECONDARY_COLOR)
+ else:
+ panel.addstr(labeling_line + 2, 0, 'Accounting:', curses.A_BOLD)
+ panel.addstr(labeling_line + 2, 12, 'Connection Closed...')
+
+ def get_title(self, width):
+ stats_label = str_tools.join(self._title_stats, ', ', width - 13)
+ return 'Bandwidth (%s):' % stats_label if stats_label else 'Bandwidth:'
+
+ def primary_header(self, width):
+ stats = ['%-14s' % ('%s/sec' % _size_label(self.last_primary * 1024))]
+
+ # if wide then avg and total are part of the header, otherwise they're on
+ # the x-axis
+
+ if width * 2 > COLLAPSE_WIDTH:
+ stats.append('- avg: %s/sec' % _size_label(self.primary_total / (time.time() - self.start_time) * 1024))
+ stats.append(', total: %s' % _size_label(self.primary_total * 1024))
+
+ stats_label = str_tools.join(stats, '', width - 12)
+
+ if stats_label:
+ return 'Download (%s):' % stats_label
+ else:
+ return 'Download:'
+
+ def secondary_header(self, width):
+ stats = ['%-14s' % ('%s/sec' % _size_label(self.last_secondary * 1024))]
+
+ # if wide then avg and total are part of the header, otherwise they're on
+ # the x-axis
+
+ if width * 2 > COLLAPSE_WIDTH:
+ stats.append('- avg: %s/sec' % _size_label(self.secondary_total / (time.time() - self.start_time) * 1024))
+ stats.append(', total: %s' % _size_label(self.secondary_total * 1024))
+
+ stats_label = str_tools.join(stats, '', width - 10)
+
+ if stats_label:
+ return 'Upload (%s):' % stats_label
+ else:
+ return 'Upload:'
+
+ def get_content_height(self):
+ base_height = GraphStats.get_content_height(self)
+ return base_height + 3 if self._accounting_stats else base_height
+
+ def new_desc_event(self, event):
+ controller = tor_controller()
+
+ if not controller.is_alive():
+ return # keep old values
+
+ my_fingerprint = controller.get_info('fingerprint', None)
+
+ if not event or (my_fingerprint and my_fingerprint in [fp for fp, _ in event.relays]):
+ stats = []
+
+ bw_rate = controller.get_effective_rate(None)
+ bw_burst = controller.get_effective_rate(None, burst = True)
+
+ if bw_rate and bw_burst:
+ bw_rate_label = _size_label(bw_rate)
+ bw_burst_label = _size_label(bw_burst)
+
+ # if both are using rounded values then strip off the '.0' decimal
+
+ if '.0' in bw_rate_label and '.0' in bw_burst_label:
+ bw_rate_label = bw_rate_label.split('.', 1)[0]
+ bw_burst_label = bw_burst_label.split('.', 1)[0]
+
+ stats.append('limit: %s/s' % bw_rate_label)
+ stats.append('burst: %s/s' % bw_burst_label)
+
+ my_router_status_entry = controller.get_network_status(default = None)
+ measured_bw = getattr(my_router_status_entry, 'bandwidth', None)
+
+ if measured_bw:
+ stats.append('measured: %s/s' % _size_label(measured_bw))
+ else:
+ my_server_descriptor = controller.get_server_descriptor(default = None)
+ observed_bw = getattr(my_server_descriptor, 'observed_bandwidth', None)
+
+ if observed_bw:
+ stats.append('observed: %s/s' % _size_label(observed_bw))
+
+ self._title_stats = stats
+
+
+class ConnStats(GraphStats):
+ """
+ Tracks number of connections, counting client and directory connections as
+ outbound. Control connections are excluded from counts.
+ """
+
+ def clone(self, new_copy=None):
+ if not new_copy:
+ new_copy = ConnStats()
+
+ return GraphStats.clone(self, new_copy)
+
+ def event_tick(self):
+ """
+ Fetches connection stats from cached information.
+ """
+
+ inbound_count, outbound_count = 0, 0
+
+ controller = tor_controller()
+
+ or_ports = controller.get_ports(Listener.OR)
+ dir_ports = controller.get_ports(Listener.DIR)
+ control_ports = controller.get_ports(Listener.CONTROL)
+
+ for entry in arm.util.tracker.get_connection_tracker().get_value():
+ local_port = entry.local_port
+
+ if local_port in or_ports or local_port in dir_ports:
+ inbound_count += 1
+ elif local_port in control_ports:
+ pass # control connection
+ else:
+ outbound_count += 1
+
+ self._process_event(inbound_count, outbound_count)
+
+ def get_title(self, width):
+ return 'Connection Count:'
+
+ def primary_header(self, width):
+ avg = self.primary_total / max(1, self.tick)
+ return 'Inbound (%s, avg: %s):' % (self.last_primary, avg)
+
+ def secondary_header(self, width):
+ avg = self.secondary_total / max(1, self.tick)
+ return 'Outbound (%s, avg: %s):' % (self.last_secondary, avg)
+
+
+class ResourceStats(GraphStats):
+ """
+ System resource usage tracker.
+ """
+
+ def __init__(self):
+ GraphStats.__init__(self)
+ self._last_counter = None
+
+ def clone(self, new_copy=None):
+ if not new_copy:
+ new_copy = ResourceStats()
+
+ return GraphStats.clone(self, new_copy)
+
+ def get_title(self, width):
+ return 'System Resources:'
+
+ def primary_header(self, width):
+ avg = self.primary_total / max(1, self.tick)
+ return 'CPU (%0.1f%%, avg: %0.1f%%):' % (self.last_primary, avg)
+
+ def secondary_header(self, width):
+ # memory sizes are converted from MB to B before generating labels
+
+ usage_label = str_tools.size_label(self.last_secondary * 1048576, 1)
+
+ avg = self.secondary_total / max(1, self.tick)
+ avg_label = str_tools.size_label(avg * 1048576, 1)
+
+ return 'Memory (%s, avg: %s):' % (usage_label, avg_label)
+
+ def event_tick(self):
+ """
+ Fetch the cached measurement of resource usage from the ResourceTracker.
+ """
+
+ resource_tracker = arm.util.tracker.get_resource_tracker()
+
+ if resource_tracker and resource_tracker.run_counter() != self._last_counter:
+ resources = resource_tracker.get_value()
+ primary = resources.cpu_sample * 100 # decimal percentage to whole numbers
+ secondary = resources.memory_bytes / 1048576 # translate size to MB so axis labels are short
+
+ self._last_counter = resource_tracker.run_counter()
+ self._process_event(primary, secondary)
+
+
+class GraphPanel(panel.Panel):
+ """
+ Panel displaying a graph, drawing statistics from custom GraphStats
+ implementations.
+ """
+
+ def __init__(self, stdscr):
+ panel.Panel.__init__(self, stdscr, 'graph', 0)
+ self.update_interval = CONFIG['features.graph.interval']
+
+ if self.update_interval < 0 or self.update_interval > len(CONFIG['attr.graph.intervals']) - 1:
+ self.update_interval = 0 # user configured it with a value that's out of bounds
+
+ self.bounds = list(Bounds)[CONFIG['features.graph.bound']]
+ self.graph_height = CONFIG['features.graph.height']
+ self.current_display = None # label of the stats currently being displayed
+
+ self.stats = {
+ GraphStat.BANDWIDTH: BandwidthStats(),
+ GraphStat.SYSTEM_RESOURCES: ResourceStats(),
+ }
+
+ if CONFIG['features.panels.show.connection']:
+ self.stats[GraphStat.CONNECTIONS] = ConnStats()
+
+ for stat in self.stats.values():
+ stat._graph_panel = self
+
+ self.set_pause_attr('stats')
+
+ try:
+ initial_stats = GRAPH_INIT_STATS.get(CONFIG['features.graph.type'])
+ self.set_stats(initial_stats)
+ except ValueError:
+ pass # invalid stats, maybe connections when lookups are disabled
+
+ # prepopulates bandwidth values from state file
+
+ if CONFIG["features.graph.bw.prepopulate"] and tor_controller().is_alive():
+ try:
+ missing_seconds = self.stats[GraphStat.BANDWIDTH].prepopulate_from_state()
+
+ if missing_seconds:
+ log.notice(msg('panel.graphing.prepopulation_successful', duration = str_tools.time_label(missing_seconds, 0, True)))
+ else:
+ log.notice(msg('panel.graphing.prepopulation_all_successful'))
+
+ self.update_interval = 4
+ except ValueError as exc:
+ log.info(msg('panel.graphing.prepopulation_failure', error = str(exc)))
+
+ def get_update_interval(self):
+ """
+ Provides the rate that we update the graph at.
+ """
+
+ return self.update_interval
+
+ def set_update_interval(self, update_interval):
+ """
+ Sets the rate that we update the graph at.
+
+ Arguments:
+ update_interval - update time enum
+ """
+
+ self.update_interval = update_interval
+
+ def get_bounds_type(self):
+ """
+ Provides the type of graph bounds used.
+ """
+
+ return self.bounds
+
+ def set_bounds_type(self, bounds_type):
+ """
+ Sets the type of graph boundaries we use.
+
+ Arguments:
+ bounds_type - graph bounds enum
+ """
+
+ self.bounds = bounds_type
+
+ def get_height(self):
+ """
+ Provides the height requested by the currently displayed GraphStats (zero
+ if hidden).
+ """
+
+ if self.current_display:
+ return self.stats[self.current_display].get_content_height() + self.graph_height
+ else:
+ return 0
+
+ def set_graph_height(self, new_graph_height):
+ """
+ Sets the preferred height used for the graph (restricted to the
+ MIN_GRAPH_HEIGHT minimum).
+
+ Arguments:
+ new_graph_height - new height for the graph
+ """
+
+ self.graph_height = max(MIN_GRAPH_HEIGHT, new_graph_height)
+
+ def resize_graph(self):
+ """
+ Prompts for user input to resize the graph panel. Options include...
+ down arrow - grow graph
+ up arrow - shrink graph
+ enter / space - set size
+ """
+
+ control = arm.controller.get_controller()
+
+ with panel.CURSES_LOCK:
+ try:
+ while True:
+ msg = 'press the down/up to resize the graph, and enter when done'
+ control.set_msg(msg, curses.A_BOLD, True)
+ curses.cbreak()
+ key = control.key_input()
+
+ if key.match('down'):
+ # don't grow the graph if it's already consuming the whole display
+ # (plus an extra line for the graph/log gap)
+
+ max_height = self.parent.getmaxyx()[0] - self.top
+ current_height = self.get_height()
+
+ if current_height < max_height + 1:
+ self.set_graph_height(self.graph_height + 1)
+ elif key.match('up'):
+ self.set_graph_height(self.graph_height - 1)
+ elif key.is_selection():
+ break
+
+ control.redraw()
+ finally:
+ control.set_msg()
+
+ def handle_key(self, key):
+ if key.match('r'):
+ self.resize_graph()
+ elif key.match('b'):
+ # uses the next boundary type
+ self.bounds = Bounds.next(self.bounds)
+ self.redraw(True)
+ elif key.match('s'):
+ # provides a menu to pick the graphed stats
+
+ available_stats = self.stats.keys()
+ available_stats.sort()
+
+ # uses sorted, camel cased labels for the options
+
+ options = ['None']
+
+ for label in available_stats:
+ words = label.split()
+ options.append(' '.join(word[0].upper() + word[1:] for word in words))
+
+ if self.current_display:
+ initial_selection = available_stats.index(self.current_display) + 1
+ else:
+ initial_selection = 0
+
+ selection = arm.popups.show_menu('Graphed Stats:', options, initial_selection)
+
+ # applies new setting
+
+ if selection == 0:
+ self.set_stats(None)
+ elif selection != -1:
+ self.set_stats(available_stats[selection - 1])
+ elif key.match('i'):
+ # provides menu to pick graph panel update interval
+
+ options = CONFIG['attr.graph.intervals'].keys()
+ selection = arm.popups.show_menu('Update Interval:', options, self.update_interval)
+
+ if selection != -1:
+ self.update_interval = selection
+ else:
+ return False
+
+ return True
+
+ def get_help(self):
+ return [
+ ('r', 'resize graph', None),
+ ('s', 'graphed stats', self.current_display if self.current_display else 'none'),
+ ('b', 'graph bounds', self.bounds.lower()),
+ ('i', 'graph update interval', CONFIG['attr.graph.intervals'].keys()[self.update_interval]),
+ ]
+
+ def draw(self, width, height):
+ if not self.current_display:
+ return
+
+ param = self.get_attr('stats')[self.current_display]
+ graph_column = min((width - 10) / 2, param.max_column)
+
+ if self.is_title_visible():
+ self.addstr(0, 0, param.get_title(width), curses.A_STANDOUT)
+
+ # top labels
+
+ left, right = param.primary_header(width / 2), param.secondary_header(width / 2)
+
+ if left:
+ self.addstr(1, 0, left, curses.A_BOLD, PRIMARY_COLOR)
+
+ if right:
+ self.addstr(1, graph_column + 5, right, curses.A_BOLD, SECONDARY_COLOR)
+
+ # determines max/min value on the graph
+
+ if self.bounds == Bounds.GLOBAL_MAX:
+ primary_max_bound = int(param.max_primary[self.update_interval])
+ secondary_max_bound = int(param.max_secondary[self.update_interval])
+ else:
+ # both Bounds.LOCAL_MAX and Bounds.TIGHT use local maxima
+ if graph_column < 2:
+ # nothing being displayed
+ primary_max_bound, secondary_max_bound = 0, 0
+ else:
+ primary_max_bound = int(max(param.primary_counts[self.update_interval][1:graph_column + 1]))
+ secondary_max_bound = int(max(param.secondary_counts[self.update_interval][1:graph_column + 1]))
+
+ primary_min_bound = secondary_min_bound = 0
+
+ if self.bounds == Bounds.TIGHT:
+ primary_min_bound = int(min(param.primary_counts[self.update_interval][1:graph_column + 1]))
+ secondary_min_bound = int(min(param.secondary_counts[self.update_interval][1:graph_column + 1]))
+
+ # if the max = min (ie, all values are the same) then use zero lower
+ # bound so a graph is still displayed
+
+ if primary_min_bound == primary_max_bound:
+ primary_min_bound = 0
+
+ if secondary_min_bound == secondary_max_bound:
+ secondary_min_bound = 0
+
+ # displays upper and lower bounds
+
+ self.addstr(2, 0, '%4i' % primary_max_bound, PRIMARY_COLOR)
+ self.addstr(self.graph_height + 1, 0, '%4i' % primary_min_bound, PRIMARY_COLOR)
+
+ self.addstr(2, graph_column + 5, '%4i' % secondary_max_bound, SECONDARY_COLOR)
+ self.addstr(self.graph_height + 1, graph_column + 5, '%4i' % secondary_min_bound, SECONDARY_COLOR)
+
+ # displays intermediate bounds on every other row
+
+ if CONFIG['features.graph.showIntermediateBounds']:
+ ticks = (self.graph_height - 3) / 2
+
+ for i in range(ticks):
+ row = self.graph_height - (2 * i) - 3
+
+ if self.graph_height % 2 == 0 and i >= (ticks / 2):
+ row -= 1
+
+ if primary_min_bound != primary_max_bound:
+ primary_val = (primary_max_bound - primary_min_bound) * (self.graph_height - row - 1) / (self.graph_height - 1)
+
+ if primary_val not in (primary_min_bound, primary_max_bound):
+ self.addstr(row + 2, 0, '%4i' % primary_val, PRIMARY_COLOR)
+
+ if secondary_min_bound != secondary_max_bound:
+ secondary_val = (secondary_max_bound - secondary_min_bound) * (self.graph_height - row - 1) / (self.graph_height - 1)
+
+ if secondary_val not in (secondary_min_bound, secondary_max_bound):
+ self.addstr(row + 2, graph_column + 5, '%4i' % secondary_val, SECONDARY_COLOR)
+
+ # creates bar graph (both primary and secondary)
+
+ for col in range(graph_column):
+ column_count = int(param.primary_counts[self.update_interval][col + 1]) - primary_min_bound
+ column_height = min(self.graph_height, self.graph_height * column_count / (max(1, primary_max_bound) - primary_min_bound))
+
+ for row in range(column_height):
+ self.addstr(self.graph_height + 1 - row, col + 5, ' ', curses.A_STANDOUT, PRIMARY_COLOR)
+
+ column_count = int(param.secondary_counts[self.update_interval][col + 1]) - secondary_min_bound
+ column_height = min(self.graph_height, self.graph_height * column_count / (max(1, secondary_max_bound) - secondary_min_bound))
+
+ for row in range(column_height):
+ self.addstr(self.graph_height + 1 - row, col + graph_column + 10, ' ', curses.A_STANDOUT, SECONDARY_COLOR)
+
+ # bottom labeling of x-axis
+
+ interval_sec = int(CONFIG['attr.graph.intervals'].values()[self.update_interval]) # seconds per labeling
+
+ interval_spacing = 10 if graph_column >= WIDE_LABELING_GRAPH_COL else 5
+ units_label, decimal_precision = None, 0
+
+ for i in range((graph_column - 4) / interval_spacing):
+ loc = (i + 1) * interval_spacing
+ time_label = str_tools.time_label(loc * interval_sec, decimal_precision)
+
+ if not units_label:
+ units_label = time_label[-1]
+ elif units_label != time_label[-1]:
+ # upped scale so also up precision of future measurements
+ units_label = time_label[-1]
+ decimal_precision += 1
+ else:
+ # if constrained on space then strips labeling since already provided
+ time_label = time_label[:-1]
+
+ self.addstr(self.graph_height + 2, 4 + loc, time_label, PRIMARY_COLOR)
+ self.addstr(self.graph_height + 2, graph_column + 10 + loc, time_label, SECONDARY_COLOR)
+
+ param.draw(self, width, height) # allows current stats to modify the display
+
+ def get_stats(self):
+ """
+ Provides the currently selected stats label.
+ """
+
+ return self.current_display
+
+ def set_stats(self, label):
+ """
+ Sets the currently displayed stats instance, hiding panel if None.
+ """
+
+ if label != self.current_display:
+ if self.current_display:
+ self.stats[self.current_display].is_selected = False
+
+ if not label:
+ self.current_display = None
+ elif label in self.stats.keys():
+ self.current_display = label
+ self.stats[self.current_display].is_selected = True
+ else:
+ raise ValueError('Unrecognized stats label: %s' % label)
+
+ def copy_attr(self, attr):
+ if attr == 'stats':
+ # uses custom clone method to copy GraphStats instances
+ return dict([(key, self.stats[key].clone()) for key in self.stats])
+ else:
+ return panel.Panel.copy_attr(self, attr)
+
+
+def _size_label(byte_count):
+ return str_tools.size_label(byte_count, 1, is_bytes = CONFIG['features.graph.bw.transferInBytes'])
diff --git a/arm/graphing/__init__.py b/arm/graphing/__init__.py
deleted file mode 100644
index 74f7e07..0000000
--- a/arm/graphing/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-Graphing panel resources.
-"""
-
-__all__ = [
- 'bandwidth_stats',
- 'conn_stats',
- 'graph_panel',
- 'resource_stats',
-]
diff --git a/arm/graphing/bandwidth_stats.py b/arm/graphing/bandwidth_stats.py
deleted file mode 100644
index 4d52aae..0000000
--- a/arm/graphing/bandwidth_stats.py
+++ /dev/null
@@ -1,285 +0,0 @@
-"""
-Tracks bandwidth usage of the tor process, expanding to include accounting
-stats if they're set.
-"""
-
-import time
-import curses
-
-import arm.controller
-
-from arm.graphing import graph_panel
-from arm.util import bandwidth_from_state, tor_controller
-
-from stem.control import State
-from stem.util import conf, str_tools, system
-
-ACCOUNTING_RATE = 5
-
-CONFIG = conf.config_dict('arm', {
- 'attr.hibernate_color': {},
- 'attr.graph.intervals': {},
- 'features.graph.bw.transferInBytes': False,
- 'features.graph.bw.accounting.show': True,
- 'tor.chroot': '',
-})
-
-# width at which panel abandons placing optional stats (avg and total) with
-# header in favor of replacing the x-axis label
-
-COLLAPSE_WIDTH = 135
-
-
-class BandwidthStats(graph_panel.GraphStats):
- """
- Uses tor BW events to generate bandwidth usage graph.
- """
-
- def __init__(self, is_pause_buffer = False):
- graph_panel.GraphStats.__init__(self)
-
- # listens for tor reload (sighup) events which can reset the bandwidth
- # rate/burst and if tor's using accounting
-
- controller = tor_controller()
- self._title_stats = []
- self._accounting_stats = None
-
- if not is_pause_buffer:
- self.reset_listener(controller, State.INIT, None) # initializes values
-
- controller.add_status_listener(self.reset_listener)
- self.new_desc_event(None) # updates title params
-
- # We both show our 'total' attributes and use it to determine our average.
- #
- # If we can get *both* our start time and the totals from tor (via 'GETINFO
- # traffic/*') then that's ideal, but if not then just track the total for
- # the time arm is run.
-
- read_total = controller.get_info('traffic/read', None)
- write_total = controller.get_info('traffic/written', None)
- start_time = system.start_time(controller.get_pid(None))
-
- if read_total and write_total and start_time:
- self.primary_total = int(read_total) / 1024 # Bytes -> KB
- self.secondary_total = int(write_total) / 1024 # Bytes -> KB
- self.start_time = start_time
- else:
- self.start_time = time.time()
-
- def clone(self, new_copy = None):
- if not new_copy:
- new_copy = BandwidthStats(True)
-
- new_copy._accounting_stats = self._accounting_stats
- new_copy._title_stats = self._title_stats
-
- return graph_panel.GraphStats.clone(self, new_copy)
-
- def reset_listener(self, controller, event_type, _):
- # updates title parameters and accounting status if they changed
-
- self.new_desc_event(None) # updates title params
-
- if event_type in (State.INIT, State.RESET) and CONFIG['features.graph.bw.accounting.show']:
- is_accounting_enabled = controller.get_info('accounting/enabled', None) == '1'
-
- if is_accounting_enabled != bool(self._accounting_stats):
- self._accounting_stats = tor_controller().get_accounting_stats(None)
-
- # redraws the whole screen since our height changed
-
- arm.controller.get_controller().redraw()
-
- # redraws to reflect changes (this especially noticeable when we have
- # accounting and shut down since it then gives notice of the shutdown)
-
- if self._graph_panel and self.is_selected:
- self._graph_panel.redraw(True)
-
- def prepopulate_from_state(self):
- """
- Attempts to use tor's state file to prepopulate values for the 15 minute
- interval via the BWHistoryReadValues/BWHistoryWriteValues values. This
- returns True if successful and False otherwise.
- """
-
- stats = bandwidth_from_state()
-
- missing_read_entries = int((time.time() - stats.last_read_time) / 900)
- missing_write_entries = int((time.time() - stats.last_write_time) / 900)
-
- # fills missing entries with the last value
-
- bw_read_entries = stats.read_entries + [stats.read_entries[-1]] * missing_read_entries
- bw_write_entries = stats.write_entries + [stats.write_entries[-1]] * missing_write_entries
-
- # crops starting entries so they're the same size
-
- entry_count = min(len(bw_read_entries), len(bw_write_entries), self.max_column)
- bw_read_entries = bw_read_entries[len(bw_read_entries) - entry_count:]
- bw_write_entries = bw_write_entries[len(bw_write_entries) - entry_count:]
-
- # gets index for 15-minute interval
-
- interval_index = 0
-
- for interval_rate in CONFIG['attr.graph.intervals'].values():
- if int(interval_rate) == 900:
- break
- else:
- interval_index += 1
-
- # fills the graphing parameters with state information
-
- for i in range(entry_count):
- read_value, write_value = bw_read_entries[i], bw_write_entries[i]
-
- self.last_primary, self.last_secondary = read_value, write_value
-
- self.primary_counts[interval_index].insert(0, read_value)
- self.secondary_counts[interval_index].insert(0, write_value)
-
- self.max_primary[interval_index] = max(self.primary_counts)
- self.max_secondary[interval_index] = max(self.secondary_counts)
-
- del self.primary_counts[interval_index][self.max_column + 1:]
- del self.secondary_counts[interval_index][self.max_column + 1:]
-
- return time.time() - min(stats.last_read_time, stats.last_write_time)
-
- def bandwidth_event(self, event):
- if self._accounting_stats and self.is_next_tick_redraw():
- if time.time() - self._accounting_stats.retrieved >= ACCOUNTING_RATE:
- self._accounting_stats = tor_controller().get_accounting_stats(None)
-
- # scales units from B to KB for graphing
-
- self._process_event(event.read / 1024.0, event.written / 1024.0)
-
- def draw(self, panel, width, height):
- # line of the graph's x-axis labeling
-
- labeling_line = graph_panel.GraphStats.get_content_height(self) + panel.graph_height - 2
-
- # if display is narrow, overwrites x-axis labels with avg / total stats
-
- if width <= COLLAPSE_WIDTH:
- # clears line
-
- panel.addstr(labeling_line, 0, ' ' * width)
- graph_column = min((width - 10) / 2, self.max_column)
-
- runtime = time.time() - self.start_time
- primary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.primary_total * 1024), _size_label(self.primary_total / runtime * 1024))
- secondary_footer = 'total: %s, avg: %s/sec' % (_size_label(self.secondary_total * 1024), _size_label(self.secondary_total / runtime * 1024))
-
- panel.addstr(labeling_line, 1, primary_footer, graph_panel.PRIMARY_COLOR)
- panel.addstr(labeling_line, graph_column + 6, secondary_footer, graph_panel.SECONDARY_COLOR)
-
- # provides accounting stats if enabled
-
- if self._accounting_stats:
- if tor_controller().is_alive():
- hibernate_color = CONFIG['attr.hibernate_color'].get(self._accounting_stats.status, 'red')
-
- x, y = 0, labeling_line + 2
- x = panel.addstr(y, x, 'Accounting (', curses.A_BOLD)
- x = panel.addstr(y, x, self._accounting_stats.status, curses.A_BOLD, hibernate_color)
- x = panel.addstr(y, x, ')', curses.A_BOLD)
-
- panel.addstr(y, 35, 'Time to reset: %s' % str_tools.short_time_label(self._accounting_stats.time_until_reset))
-
- panel.addstr(y + 1, 2, '%s / %s' % (self._accounting_stats.read_bytes, self._accounting_stats.read_limit), graph_panel.PRIMARY_COLOR)
- panel.addstr(y + 1, 37, '%s / %s' % (self._accounting_stats.written_bytes, self._accounting_stats.write_limit), graph_panel.SECONDARY_COLOR)
- else:
- panel.addstr(labeling_line + 2, 0, 'Accounting:', curses.A_BOLD)
- panel.addstr(labeling_line + 2, 12, 'Connection Closed...')
-
- def get_title(self, width):
- stats_label = str_tools.join(self._title_stats, ', ', width - 13)
- return 'Bandwidth (%s):' % stats_label if stats_label else 'Bandwidth:'
-
- def primary_header(self, width):
- stats = ['%-14s' % ('%s/sec' % _size_label(self.last_primary * 1024))]
-
- # if wide then avg and total are part of the header, otherwise they're on
- # the x-axis
-
- if width * 2 > COLLAPSE_WIDTH:
- stats.append('- avg: %s/sec' % _size_label(self.primary_total / (time.time() - self.start_time) * 1024))
- stats.append(', total: %s' % _size_label(self.primary_total * 1024))
-
- stats_label = str_tools.join(stats, '', width - 12)
-
- if stats_label:
- return 'Download (%s):' % stats_label
- else:
- return 'Download:'
-
- def secondary_header(self, width):
- stats = ['%-14s' % ('%s/sec' % _size_label(self.last_secondary * 1024))]
-
- # if wide then avg and total are part of the header, otherwise they're on
- # the x-axis
-
- if width * 2 > COLLAPSE_WIDTH:
- stats.append('- avg: %s/sec' % _size_label(self.secondary_total / (time.time() - self.start_time) * 1024))
- stats.append(', total: %s' % _size_label(self.secondary_total * 1024))
-
- stats_label = str_tools.join(stats, '', width - 10)
-
- if stats_label:
- return 'Upload (%s):' % stats_label
- else:
- return 'Upload:'
-
- def get_content_height(self):
- base_height = graph_panel.GraphStats.get_content_height(self)
- return base_height + 3 if self._accounting_stats else base_height
-
- def new_desc_event(self, event):
- controller = tor_controller()
-
- if not controller.is_alive():
- return # keep old values
-
- my_fingerprint = controller.get_info('fingerprint', None)
-
- if not event or (my_fingerprint and my_fingerprint in [fp for fp, _ in event.relays]):
- stats = []
-
- bw_rate = controller.get_effective_rate(None)
- bw_burst = controller.get_effective_rate(None, burst = True)
-
- if bw_rate and bw_burst:
- bw_rate_label = _size_label(bw_rate)
- bw_burst_label = _size_label(bw_burst)
-
- # if both are using rounded values then strip off the '.0' decimal
-
- if '.0' in bw_rate_label and '.0' in bw_burst_label:
- bw_rate_label = bw_rate_label.split('.', 1)[0]
- bw_burst_label = bw_burst_label.split('.', 1)[0]
-
- stats.append('limit: %s/s' % bw_rate_label)
- stats.append('burst: %s/s' % bw_burst_label)
-
- my_router_status_entry = controller.get_network_status(default = None)
- measured_bw = getattr(my_router_status_entry, 'bandwidth', None)
-
- if measured_bw:
- stats.append('measured: %s/s' % _size_label(measured_bw))
- else:
- my_server_descriptor = controller.get_server_descriptor(default = None)
- observed_bw = getattr(my_server_descriptor, 'observed_bandwidth', None)
-
- if observed_bw:
- stats.append('observed: %s/s' % _size_label(observed_bw))
-
- self._title_stats = stats
-
-
-def _size_label(byte_count):
- return str_tools.size_label(byte_count, 1, is_bytes = CONFIG['features.graph.bw.transferInBytes'])
diff --git a/arm/graphing/conn_stats.py b/arm/graphing/conn_stats.py
deleted file mode 100644
index c5b1c83..0000000
--- a/arm/graphing/conn_stats.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""
-Tracks stats concerning tor's current connections.
-"""
-
-import arm.util.tracker
-
-from arm.graphing import graph_panel
-from arm.util import tor_controller
-
-from stem.control import Listener
-
-
-class ConnStats(graph_panel.GraphStats):
- """
- Tracks number of connections, counting client and directory connections as
- outbound. Control connections are excluded from counts.
- """
-
- def clone(self, new_copy=None):
- if not new_copy:
- new_copy = ConnStats()
-
- return graph_panel.GraphStats.clone(self, new_copy)
-
- def event_tick(self):
- """
- Fetches connection stats from cached information.
- """
-
- inbound_count, outbound_count = 0, 0
-
- controller = tor_controller()
-
- or_ports = controller.get_ports(Listener.OR)
- dir_ports = controller.get_ports(Listener.DIR)
- control_ports = controller.get_ports(Listener.CONTROL)
-
- for entry in arm.util.tracker.get_connection_tracker().get_value():
- local_port = entry.local_port
-
- if local_port in or_ports or local_port in dir_ports:
- inbound_count += 1
- elif local_port in control_ports:
- pass # control connection
- else:
- outbound_count += 1
-
- self._process_event(inbound_count, outbound_count)
-
- def get_title(self, width):
- return 'Connection Count:'
-
- def primary_header(self, width):
- avg = self.primary_total / max(1, self.tick)
- return 'Inbound (%s, avg: %s):' % (self.last_primary, avg)
-
- def secondary_header(self, width):
- avg = self.secondary_total / max(1, self.tick)
- return 'Outbound (%s, avg: %s):' % (self.last_secondary, avg)
diff --git a/arm/graphing/graph_panel.py b/arm/graphing/graph_panel.py
deleted file mode 100644
index 77f9404..0000000
--- a/arm/graphing/graph_panel.py
+++ /dev/null
@@ -1,581 +0,0 @@
-"""
-Flexible panel for presenting bar graphs for a variety of stats. This panel is
-just concerned with the rendering of information, which is actually collected
-and stored by implementations of the GraphStats interface. Panels are made up
-of a title, followed by headers and graphs for two sets of stats. For
-instance...
-
-Bandwidth (cap: 5 MB, burst: 10 MB):
-Downloaded (0.0 B/sec): Uploaded (0.0 B/sec):
- 34 30
- * *
- ** * * * **
- * * * ** ** ** *** ** ** ** **
- ********* ****** ****** ********* ****** ******
- 0 ************ **************** 0 ************ ****************
- 25s 50 1m 1.6 2.0 25s 50 1m 1.6 2.0
-"""
-
-import copy
-import curses
-
-import arm.popups
-import arm.controller
-
-import stem.control
-
-from arm.util import msg, panel, tor_controller
-
-from stem.util import conf, enum, log, str_tools
-
-GraphStat = enum.Enum('BANDWIDTH', 'CONNECTIONS', 'SYSTEM_RESOURCES')
-
-# maps 'features.graph.type' config values to the initial types
-
-GRAPH_INIT_STATS = {1: GraphStat.BANDWIDTH, 2: GraphStat.CONNECTIONS, 3: GraphStat.SYSTEM_RESOURCES}
-
-DEFAULT_CONTENT_HEIGHT = 4 # space needed for labeling above and below the graph
-PRIMARY_COLOR, SECONDARY_COLOR = 'green', 'cyan'
-MIN_GRAPH_HEIGHT = 1
-
-# enums for graph bounds:
-# Bounds.GLOBAL_MAX - global maximum (highest value ever seen)
-# Bounds.LOCAL_MAX - local maximum (highest value currently on the graph)
-# Bounds.TIGHT - local maximum and minimum
-
-Bounds = enum.Enum('GLOBAL_MAX', 'LOCAL_MAX', 'TIGHT')
-
-WIDE_LABELING_GRAPH_COL = 50 # minimum graph columns to use wide spacing for x-axis labels
-
-
-def conf_handler(key, value):
- if key == 'features.graph.height':
- return max(MIN_GRAPH_HEIGHT, value)
- elif key == 'features.graph.max_width':
- return max(1, value)
- elif key == 'features.graph.bound':
- return max(0, min(2, value))
-
-
-# used for setting defaults when initializing GraphStats and GraphPanel instances
-
-CONFIG = conf.config_dict('arm', {
- 'attr.graph.intervals': {},
- 'features.graph.height': 7,
- 'features.graph.interval': 0,
- 'features.graph.bound': 1,
- 'features.graph.max_width': 150,
- 'features.graph.showIntermediateBounds': True,
- 'features.graph.type': 1,
- 'features.panels.show.connection': True,
- 'features.graph.bw.prepopulate': True,
-}, conf_handler)
-
-
-class GraphStats:
- """
- Module that's expected to update dynamically and provide attributes to be
- graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
- time and timescale parameters use the labels defined in CONFIG['attr.graph.intervals'].
- """
-
- def __init__(self):
- """
- Initializes parameters needed to present a graph.
- """
-
- # panel to be redrawn when updated (set when added to GraphPanel)
-
- self._graph_panel = None
- self.is_selected = False
- self.is_pause_buffer = False
-
- # tracked stats
-
- self.tick = 0 # number of processed events
- self.last_primary, self.last_secondary = 0, 0 # most recent registered stats
- self.primary_total, self.secondary_total = 0, 0 # sum of all stats seen
-
- # timescale dependent stats
-
- self.max_column = CONFIG['features.graph.max_width']
- self.max_primary, self.max_secondary = {}, {}
- self.primary_counts, self.secondary_counts = {}, {}
-
- for i in range(len(CONFIG['attr.graph.intervals'])):
- # recent rates for graph
-
- self.max_primary[i] = 0
- self.max_secondary[i] = 0
-
- # historic stats for graph, first is accumulator
- # iterative insert needed to avoid making shallow copies (nasty, nasty gotcha)
-
- self.primary_counts[i] = (self.max_column + 1) * [0]
- self.secondary_counts[i] = (self.max_column + 1) * [0]
-
- # tracks BW events
-
- tor_controller().add_event_listener(self.bandwidth_event, stem.control.EventType.BW)
-
- def clone(self, new_copy=None):
- """
- Provides a deep copy of this instance.
-
- Arguments:
- new_copy - base instance to build copy off of
- """
-
- if not new_copy:
- new_copy = GraphStats()
-
- new_copy.tick = self.tick
- new_copy.last_primary = self.last_primary
- new_copy.last_secondary = self.last_secondary
- new_copy.primary_total = self.primary_total
- new_copy.secondary_total = self.secondary_total
- new_copy.max_primary = dict(self.max_primary)
- new_copy.max_secondary = dict(self.max_secondary)
- new_copy.primary_counts = copy.deepcopy(self.primary_counts)
- new_copy.secondary_counts = copy.deepcopy(self.secondary_counts)
- new_copy.is_pause_buffer = True
- return new_copy
-
- def event_tick(self):
- """
- Called when it's time to process another event. All graphs use tor BW
- events to keep in sync with each other (this happens once a second).
- """
-
- pass
-
- def is_next_tick_redraw(self):
- """
- Provides true if the following tick (call to _process_event) will result in
- being redrawn.
- """
-
- if self._graph_panel and self.is_selected and not self._graph_panel.is_paused():
- # use the minimum of the current refresh rate and the panel's
- update_rate = int(CONFIG['attr.graph.intervals'].values()[self._graph_panel.update_interval])
- return (self.tick + 1) % update_rate == 0
- else:
- return False
-
- def get_title(self, width):
- """
- Provides top label.
- """
-
- return ''
-
- def primary_header(self, width):
- return ''
-
- def secondary_header(self, width):
- return ''
-
- def get_content_height(self):
- """
- Provides the height content should take up (not including the graph).
- """
-
- return DEFAULT_CONTENT_HEIGHT
-
- def draw(self, panel, width, height):
- """
- Allows for any custom drawing monitor wishes to append.
- """
-
- pass
-
- def bandwidth_event(self, event):
- if not self.is_pause_buffer:
- self.event_tick()
-
- def _process_event(self, primary, secondary):
- """
- Includes new stats in graphs and notifies associated GraphPanel of changes.
- """
-
- is_redraw = self.is_next_tick_redraw()
-
- self.last_primary, self.last_secondary = primary, secondary
- self.primary_total += primary
- self.secondary_total += secondary
-
- # updates for all time intervals
-
- self.tick += 1
-
- for i in range(len(CONFIG['attr.graph.intervals'])):
- lable, timescale = CONFIG['attr.graph.intervals'].items()[i]
- timescale = int(timescale)
-
- self.primary_counts[i][0] += primary
- self.secondary_counts[i][0] += secondary
-
- if self.tick % timescale == 0:
- self.max_primary[i] = max(self.max_primary[i], self.primary_counts[i][0] / timescale)
- self.primary_counts[i][0] /= timescale
- self.primary_counts[i].insert(0, 0)
- del self.primary_counts[i][self.max_column + 1:]
-
- self.max_secondary[i] = max(self.max_secondary[i], self.secondary_counts[i][0] / timescale)
- self.secondary_counts[i][0] /= timescale
- self.secondary_counts[i].insert(0, 0)
- del self.secondary_counts[i][self.max_column + 1:]
-
- if is_redraw and self._graph_panel:
- self._graph_panel.redraw(True)
-
-
-class GraphPanel(panel.Panel):
- """
- Panel displaying a graph, drawing statistics from custom GraphStats
- implementations.
- """
-
- def __init__(self, stdscr):
- panel.Panel.__init__(self, stdscr, 'graph', 0)
- self.update_interval = CONFIG['features.graph.interval']
-
- if self.update_interval < 0 or self.update_interval > len(CONFIG['attr.graph.intervals']) - 1:
- self.update_interval = 0 # user configured it with a value that's out of bounds
-
- self.bounds = list(Bounds)[CONFIG['features.graph.bound']]
- self.graph_height = CONFIG['features.graph.height']
- self.current_display = None # label of the stats currently being displayed
-
- self.stats = {
- GraphStat.BANDWIDTH: arm.graphing.bandwidth_stats.BandwidthStats(),
- GraphStat.SYSTEM_RESOURCES: arm.graphing.resource_stats.ResourceStats(),
- }
-
- if CONFIG['features.panels.show.connection']:
- self.stats[GraphStat.CONNECTIONS] = arm.graphing.conn_stats.ConnStats()
-
- for stat in self.stats.values():
- stat._graph_panel = self
-
- self.set_pause_attr('stats')
-
- try:
- initial_stats = GRAPH_INIT_STATS.get(CONFIG['features.graph.type'])
- self.set_stats(initial_stats)
- except ValueError:
- pass # invalid stats, maybe connections when lookups are disabled
-
- # prepopulates bandwidth values from state file
-
- if CONFIG["features.graph.bw.prepopulate"] and tor_controller().is_alive():
- try:
- missing_seconds = self.stats[GraphStat.BANDWIDTH].prepopulate_from_state()
-
- if missing_seconds:
- log.notice(msg('panel.graphing.prepopulation_successful', duration = str_tools.time_label(missing_seconds, 0, True)))
- else:
- log.notice(msg('panel.graphing.prepopulation_all_successful'))
-
- self.update_interval = 4
- except ValueError as exc:
- log.info(msg('panel.graphing.prepopulation_failure', error = str(exc)))
-
- def get_update_interval(self):
- """
- Provides the rate that we update the graph at.
- """
-
- return self.update_interval
-
- def set_update_interval(self, update_interval):
- """
- Sets the rate that we update the graph at.
-
- Arguments:
- update_interval - update time enum
- """
-
- self.update_interval = update_interval
-
- def get_bounds_type(self):
- """
- Provides the type of graph bounds used.
- """
-
- return self.bounds
-
- def set_bounds_type(self, bounds_type):
- """
- Sets the type of graph boundaries we use.
-
- Arguments:
- bounds_type - graph bounds enum
- """
-
- self.bounds = bounds_type
-
- def get_height(self):
- """
- Provides the height requested by the currently displayed GraphStats (zero
- if hidden).
- """
-
- if self.current_display:
- return self.stats[self.current_display].get_content_height() + self.graph_height
- else:
- return 0
-
- def set_graph_height(self, new_graph_height):
- """
- Sets the preferred height used for the graph (restricted to the
- MIN_GRAPH_HEIGHT minimum).
-
- Arguments:
- new_graph_height - new height for the graph
- """
-
- self.graph_height = max(MIN_GRAPH_HEIGHT, new_graph_height)
-
- def resize_graph(self):
- """
- Prompts for user input to resize the graph panel. Options include...
- down arrow - grow graph
- up arrow - shrink graph
- enter / space - set size
- """
-
- control = arm.controller.get_controller()
-
- with panel.CURSES_LOCK:
- try:
- while True:
- msg = 'press the down/up to resize the graph, and enter when done'
- control.set_msg(msg, curses.A_BOLD, True)
- curses.cbreak()
- key = control.key_input()
-
- if key.match('down'):
- # don't grow the graph if it's already consuming the whole display
- # (plus an extra line for the graph/log gap)
-
- max_height = self.parent.getmaxyx()[0] - self.top
- current_height = self.get_height()
-
- if current_height < max_height + 1:
- self.set_graph_height(self.graph_height + 1)
- elif key.match('up'):
- self.set_graph_height(self.graph_height - 1)
- elif key.is_selection():
- break
-
- control.redraw()
- finally:
- control.set_msg()
-
- def handle_key(self, key):
- if key.match('r'):
- self.resize_graph()
- elif key.match('b'):
- # uses the next boundary type
- self.bounds = Bounds.next(self.bounds)
- self.redraw(True)
- elif key.match('s'):
- # provides a menu to pick the graphed stats
-
- available_stats = self.stats.keys()
- available_stats.sort()
-
- # uses sorted, camel cased labels for the options
-
- options = ['None']
-
- for label in available_stats:
- words = label.split()
- options.append(' '.join(word[0].upper() + word[1:] for word in words))
-
- if self.current_display:
- initial_selection = available_stats.index(self.current_display) + 1
- else:
- initial_selection = 0
-
- selection = arm.popups.show_menu('Graphed Stats:', options, initial_selection)
-
- # applies new setting
-
- if selection == 0:
- self.set_stats(None)
- elif selection != -1:
- self.set_stats(available_stats[selection - 1])
- elif key.match('i'):
- # provides menu to pick graph panel update interval
-
- options = CONFIG['attr.graph.intervals'].keys()
- selection = arm.popups.show_menu('Update Interval:', options, self.update_interval)
-
- if selection != -1:
- self.update_interval = selection
- else:
- return False
-
- return True
-
- def get_help(self):
- return [
- ('r', 'resize graph', None),
- ('s', 'graphed stats', self.current_display if self.current_display else 'none'),
- ('b', 'graph bounds', self.bounds.lower()),
- ('i', 'graph update interval', CONFIG['attr.graph.intervals'].keys()[self.update_interval]),
- ]
-
- def draw(self, width, height):
- if not self.current_display:
- return
-
- param = self.get_attr('stats')[self.current_display]
- graph_column = min((width - 10) / 2, param.max_column)
-
- if self.is_title_visible():
- self.addstr(0, 0, param.get_title(width), curses.A_STANDOUT)
-
- # top labels
-
- left, right = param.primary_header(width / 2), param.secondary_header(width / 2)
-
- if left:
- self.addstr(1, 0, left, curses.A_BOLD, PRIMARY_COLOR)
-
- if right:
- self.addstr(1, graph_column + 5, right, curses.A_BOLD, SECONDARY_COLOR)
-
- # determines max/min value on the graph
-
- if self.bounds == Bounds.GLOBAL_MAX:
- primary_max_bound = int(param.max_primary[self.update_interval])
- secondary_max_bound = int(param.max_secondary[self.update_interval])
- else:
- # both Bounds.LOCAL_MAX and Bounds.TIGHT use local maxima
- if graph_column < 2:
- # nothing being displayed
- primary_max_bound, secondary_max_bound = 0, 0
- else:
- primary_max_bound = int(max(param.primary_counts[self.update_interval][1:graph_column + 1]))
- secondary_max_bound = int(max(param.secondary_counts[self.update_interval][1:graph_column + 1]))
-
- primary_min_bound = secondary_min_bound = 0
-
- if self.bounds == Bounds.TIGHT:
- primary_min_bound = int(min(param.primary_counts[self.update_interval][1:graph_column + 1]))
- secondary_min_bound = int(min(param.secondary_counts[self.update_interval][1:graph_column + 1]))
-
- # if the max = min (ie, all values are the same) then use zero lower
- # bound so a graph is still displayed
-
- if primary_min_bound == primary_max_bound:
- primary_min_bound = 0
-
- if secondary_min_bound == secondary_max_bound:
- secondary_min_bound = 0
-
- # displays upper and lower bounds
-
- self.addstr(2, 0, '%4i' % primary_max_bound, PRIMARY_COLOR)
- self.addstr(self.graph_height + 1, 0, '%4i' % primary_min_bound, PRIMARY_COLOR)
-
- self.addstr(2, graph_column + 5, '%4i' % secondary_max_bound, SECONDARY_COLOR)
- self.addstr(self.graph_height + 1, graph_column + 5, '%4i' % secondary_min_bound, SECONDARY_COLOR)
-
- # displays intermediate bounds on every other row
-
- if CONFIG['features.graph.showIntermediateBounds']:
- ticks = (self.graph_height - 3) / 2
-
- for i in range(ticks):
- row = self.graph_height - (2 * i) - 3
-
- if self.graph_height % 2 == 0 and i >= (ticks / 2):
- row -= 1
-
- if primary_min_bound != primary_max_bound:
- primary_val = (primary_max_bound - primary_min_bound) * (self.graph_height - row - 1) / (self.graph_height - 1)
-
- if primary_val not in (primary_min_bound, primary_max_bound):
- self.addstr(row + 2, 0, '%4i' % primary_val, PRIMARY_COLOR)
-
- if secondary_min_bound != secondary_max_bound:
- secondary_val = (secondary_max_bound - secondary_min_bound) * (self.graph_height - row - 1) / (self.graph_height - 1)
-
- if secondary_val not in (secondary_min_bound, secondary_max_bound):
- self.addstr(row + 2, graph_column + 5, '%4i' % secondary_val, SECONDARY_COLOR)
-
- # creates bar graph (both primary and secondary)
-
- for col in range(graph_column):
- column_count = int(param.primary_counts[self.update_interval][col + 1]) - primary_min_bound
- column_height = min(self.graph_height, self.graph_height * column_count / (max(1, primary_max_bound) - primary_min_bound))
-
- for row in range(column_height):
- self.addstr(self.graph_height + 1 - row, col + 5, ' ', curses.A_STANDOUT, PRIMARY_COLOR)
-
- column_count = int(param.secondary_counts[self.update_interval][col + 1]) - secondary_min_bound
- column_height = min(self.graph_height, self.graph_height * column_count / (max(1, secondary_max_bound) - secondary_min_bound))
-
- for row in range(column_height):
- self.addstr(self.graph_height + 1 - row, col + graph_column + 10, ' ', curses.A_STANDOUT, SECONDARY_COLOR)
-
- # bottom labeling of x-axis
-
- interval_sec = int(CONFIG['attr.graph.intervals'].values()[self.update_interval]) # seconds per labeling
-
- interval_spacing = 10 if graph_column >= WIDE_LABELING_GRAPH_COL else 5
- units_label, decimal_precision = None, 0
-
- for i in range((graph_column - 4) / interval_spacing):
- loc = (i + 1) * interval_spacing
- time_label = str_tools.time_label(loc * interval_sec, decimal_precision)
-
- if not units_label:
- units_label = time_label[-1]
- elif units_label != time_label[-1]:
- # upped scale so also up precision of future measurements
- units_label = time_label[-1]
- decimal_precision += 1
- else:
- # if constrained on space then strips labeling since already provided
- time_label = time_label[:-1]
-
- self.addstr(self.graph_height + 2, 4 + loc, time_label, PRIMARY_COLOR)
- self.addstr(self.graph_height + 2, graph_column + 10 + loc, time_label, SECONDARY_COLOR)
-
- param.draw(self, width, height) # allows current stats to modify the display
-
- def get_stats(self):
- """
- Provides the currently selected stats label.
- """
-
- return self.current_display
-
- def set_stats(self, label):
- """
- Sets the currently displayed stats instance, hiding panel if None.
- """
-
- if label != self.current_display:
- if self.current_display:
- self.stats[self.current_display].is_selected = False
-
- if not label:
- self.current_display = None
- elif label in self.stats.keys():
- self.current_display = label
- self.stats[self.current_display].is_selected = True
- else:
- raise ValueError('Unrecognized stats label: %s' % label)
-
- def copy_attr(self, attr):
- if attr == 'stats':
- # uses custom clone method to copy GraphStats instances
- return dict([(key, self.stats[key].clone()) for key in self.stats])
- else:
- return panel.Panel.copy_attr(self, attr)
diff --git a/arm/graphing/resource_stats.py b/arm/graphing/resource_stats.py
deleted file mode 100644
index d38803a..0000000
--- a/arm/graphing/resource_stats.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
-Tracks the system resource usage (cpu and memory) of the tor process.
-"""
-
-import arm.util.tracker
-
-from arm.graphing import graph_panel
-
-from stem.util import str_tools
-
-
-class ResourceStats(graph_panel.GraphStats):
- """
- System resource usage tracker.
- """
-
- def __init__(self):
- graph_panel.GraphStats.__init__(self)
- self._last_counter = None
-
- def clone(self, new_copy=None):
- if not new_copy:
- new_copy = ResourceStats()
-
- return graph_panel.GraphStats.clone(self, new_copy)
-
- def get_title(self, width):
- return 'System Resources:'
-
- def primary_header(self, width):
- avg = self.primary_total / max(1, self.tick)
- return 'CPU (%0.1f%%, avg: %0.1f%%):' % (self.last_primary, avg)
-
- def secondary_header(self, width):
- # memory sizes are converted from MB to B before generating labels
-
- usage_label = str_tools.size_label(self.last_secondary * 1048576, 1)
-
- avg = self.secondary_total / max(1, self.tick)
- avg_label = str_tools.size_label(avg * 1048576, 1)
-
- return 'Memory (%s, avg: %s):' % (usage_label, avg_label)
-
- def event_tick(self):
- """
- Fetch the cached measurement of resource usage from the ResourceTracker.
- """
-
- resource_tracker = arm.util.tracker.get_resource_tracker()
-
- if resource_tracker and resource_tracker.run_counter() != self._last_counter:
- resources = resource_tracker.get_value()
- primary = resources.cpu_sample * 100 # decimal percentage to whole numbers
- secondary = resources.memory_bytes / 1048576 # translate size to MB so axis labels are short
-
- self._last_counter = resource_tracker.run_counter()
- self._process_event(primary, secondary)
diff --git a/arm/menu/actions.py b/arm/menu/actions.py
index b95f1fc..d4323b3 100644
--- a/arm/menu/actions.py
+++ b/arm/menu/actions.py
@@ -7,7 +7,7 @@ import functools
import arm.popups
import arm.controller
import arm.menu.item
-import arm.graphing.graph_panel
+import arm.graph_panel
import arm.util.tracker
from arm.util import tor_controller, ui_tools
@@ -182,7 +182,7 @@ def make_graph_menu(graph_panel):
bounds_menu = arm.menu.item.Submenu("Bounds")
bounds_group = arm.menu.item.SelectionGroup(graph_panel.set_bounds_type, graph_panel.get_bounds_type())
- for bounds_type in arm.graphing.graph_panel.Bounds:
+ for bounds_type in arm.graph_panel.Bounds:
bounds_menu.add(arm.menu.item.SelectionMenuItem(bounds_type, bounds_group, bounds_type))
graph_menu.add(bounds_menu)
[View Less]
1
0