]> git.ipfire.org Git - people/shoehn/ipfire.org.git/commitdiff
Big update of the fireinfo service.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 26 Dec 2010 17:06:42 +0000 (18:06 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 26 Dec 2010 17:06:42 +0000 (18:06 +0100)
Very hard to break down and not neccessary.

21 files changed:
www/static/css/style.css
www/static/images/icons/bus-pci.png [new file with mode: 0644]
www/static/images/icons/bus-usb.png [new file with mode: 0644]
www/templates/modules/stasy-table-devices.html [new file with mode: 0644]
www/templates/modules/stasy-table.html
www/templates/stasy-index.html
www/templates/stasy-model-detail.html [new file with mode: 0644]
www/templates/stasy-profile-detail.html [new file with mode: 0644]
www/templates/stasy-profile.html
www/templates/stasy-stats-cpu-flags.html [new file with mode: 0644]
www/templates/stasy-stats-cpus.html
www/templates/stasy-stats-geo.html [new file with mode: 0644]
www/templates/stasy-stats-memory.html [new file with mode: 0644]
www/templates/stasy-stats-oses.html [new file with mode: 0644]
www/templates/stasy-stats-virtual.html
www/templates/stasy-stats.html
www/templates/stasy-vendor-detail.html [new file with mode: 0644]
www/webapp/__init__.py
www/webapp/backend/stasy.py
www/webapp/handlers_stasy.py
www/webapp/ui_modules.py

index 08d57cd76b6dde4a4cb3bb2d6f02736e2a865b6a..c0682f9f7945d55de3b3282478e4f493e09bb101 100644 (file)
@@ -916,3 +916,57 @@ table.blocks td.block1,td.block3 {
 table.blocks td.block2 {
        background-color: #f5f5f5;
 }
+
+table.fireinfo {
+       width: 45em;
+       margin-left: 1.5em;
+}
+
+table.fireinfo td.key {
+       width: 12.5em;
+}
+
+table.fireinfo td.value {
+       width: 32.5em;
+}
+
+table.cpufeatures tr {
+       line-height: 1.2em;
+}
+
+table.cpufeatures td {
+       padding-left: 0.1em;
+       padding-right: 0.1em;
+}
+
+table.cpufeatures td.enabled {
+       border: 1px solid #88ff88;
+       background-color: #aaffaa;
+}
+
+table.cpufeatures td.disabled {
+       border: 1px solid #ff8888;
+       background-color: #ff9999;
+}
+
+table.stats {
+       width: 45em;
+       margin-left: 1.5em;
+}
+
+table.stats td.key {
+       width: 12.5em;
+}
+
+table.stats td.value {
+       width: 2.5em;
+       text-align: right;
+}
+
+table.stats td.bar {
+       width: 30em;
+}
+
+table.stats td.bar p {
+       background-color: #880400;
+}
diff --git a/www/static/images/icons/bus-pci.png b/www/static/images/icons/bus-pci.png
new file mode 100644 (file)
index 0000000..ee746a7
Binary files /dev/null and b/www/static/images/icons/bus-pci.png differ
diff --git a/www/static/images/icons/bus-usb.png b/www/static/images/icons/bus-usb.png
new file mode 100644 (file)
index 0000000..d07c454
Binary files /dev/null and b/www/static/images/icons/bus-usb.png differ
diff --git a/www/templates/modules/stasy-table-devices.html b/www/templates/modules/stasy-table-devices.html
new file mode 100644 (file)
index 0000000..d368b9b
--- /dev/null
@@ -0,0 +1,30 @@
+<table class="fireinfo">
+       {% for group, devices in groups %}
+               <tr class="vendor">
+                       <td colspan="3">
+                               &nbsp;<br />
+                               <strong>{{ _(group) }}</strong>
+                       </td>
+               </tr>
+               {% for device in devices %}
+                       <tr class="device">
+                               <td>
+                                       &nbsp; <!-- XXX add margin in CSS -->
+                                       <img src="{{ static_url("images/icons/bus-%s.png" % device.subsystem) }}"
+                                               alt="{{ device.subsystem.upper() }}" />
+                               </td>
+                               <td>
+                                       <a class="vendor" href="/vendor/{{ device.subsystem }}/{{ device.vendor }}">
+                                               {{ device.vendor_string }}
+                                       </a>
+                                       -
+                                       <a href="/model/{{ device.subsystem }}/{{ device.vendor }}/{{ device.model }}">
+                                               {{ device.model_string or "N/A (%s)" % device.model }}
+                                       </a>
+                                       <br />
+                                       {{ _("Module") }}: {{ device.driver or "N/A" }}
+                               </td>
+                       </tr>
+               {% end %}
+       {% end %}
+</table>
index 267179a09ceacd41a3d0936a33ec5b9c94f965df..0c1353ed68d5727b08cd4b4bee12bfb8aa2eee71 100644 (file)
@@ -1,8 +1,10 @@
-<table>
+<table class="stats">
        {% for k, v in items %}
                <tr>
-                       <td>{{ k }}</td>
-                       <td>{{ "%.2f" % v }}%</td>
+                       <td class="key">{{ k }}</td>
+                       <td class="bar"><p style="{{ "width: %.2f%%" % v }}">&nbsp;</p></td>
+                       <td class="value">{{ "%.2f" % v }}%</td>
                </tr>
        {% end %}
 </table>
+<br class="clear" />
index 491dbedfcf5f9e2c109c4e6b5c7ff19b88656a2b..0f847965fc6ab973a9824f1d13da54c4d16a9c22 100644 (file)
@@ -5,8 +5,25 @@
 {% block content %}
        <h3>{{ _("Statistical evaluation service") }}</h3>
 
-       {% for p in profiles %}
-               <a href="/profile/{{ p }}">{{ p }}</a>
-       {% end %}
+       <p>
+               Please enter your profile ID and have a look of the hardware components
+               that are installed on your system:
+       </p>
+
+       <form name="profile" method="POST">
+               {{ xsrf_form_html() }}
+               <p>
+                       <input type="text" name="profile_id" size="40" maxlength="40" />
+                       <input type="submit" value="{{ _("Show profile") }}" />
+               </p>
+       </form>
+       <br class="clear" />
+
+       <h3>XXX Just for now...</h3>
+       <p>
+               {% for p in profiles %}
+                       <a href="/profile/{{ p }}">{{ p }}</a>
+               {% end %}
+       </p>
 
 {% end block %}
diff --git a/www/templates/stasy-model-detail.html b/www/templates/stasy-model-detail.html
new file mode 100644 (file)
index 0000000..643846a
--- /dev/null
@@ -0,0 +1,9 @@
+{% extends "stasy-base-1.html" %}
+
+{% block content %}
+       <h3>{{ vendor_name }} - {{ model_name or model_id  }}</h3>
+       <p>
+               This device is installed on approximately {{ "%.2f" % percentage }}% of
+               all systems in the database.
+       </p>
+{% end block %}
diff --git a/www/templates/stasy-profile-detail.html b/www/templates/stasy-profile-detail.html
new file mode 100644 (file)
index 0000000..3fe5047
--- /dev/null
@@ -0,0 +1,209 @@
+{% extends "stasy-base-2.html" %}
+
+{% block title %}{{ _("Profile") }} {{ profile.public_id }}{% end block %}
+
+{% block content %}
+       <h3>{{ _("Profile") }} {{ profile.public_id }}</h3>
+
+       <img src="{{ static_url("images/flags/%s.png" % profile.country_code) }}"
+               class="floatTR" alt="{{ profile.country_code }}" />
+
+       <table class="fireinfo">
+               <tr>
+                       <td class="key">
+                               {{ _("Vendor") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.vendor }}
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Model") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.model }}
+                       </td>
+               </tr>
+       </table>
+
+       <h4>{{ _("Operating system") }}</h4>
+       <table class="fireinfo">
+               <tr>
+                       <td class="key">
+                               {{ _("Version") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.release }}
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Architecture") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.cpu.arch }}
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Kernel version") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.kernel }}
+                       </td>
+               </tr>
+       </table>
+
+       {% if profile.hypervisor %}
+               <h4>{{ _("Hypervisor") }}</h4>
+               <p>
+                       {{ _("This machine is running in a virtual environment.") }}
+               </p>
+
+               <table class="fireinfo">
+                       <tr>
+                               <td class="key">
+                                       {{ _("Vendor") }}
+                               </td>
+                               <td class="value">
+                                       {{ profile.hypervisor.vendor }}
+                               </td>
+                       </tr>
+                       <tr>
+                               <td class="key">
+                                       {{ _("Type") }}
+                               </td>
+                               <td class="value">
+                                       {{ profile.hypervisor.type }}
+                               </td>
+                       </tr>
+               </table>
+       {% end %}
+       
+       <h4>{{ _("Hardware") }}</h4>
+       <table class="fireinfo">
+               <tr>
+                       <td colspan="2">
+                               <strong>{{ _("CPU") }}</strong>
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Vendor") }}
+                       </td>
+                       <td class="value">
+                               <a href="/stats/cpus">{{ profile.cpu.vendor }}</a>
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Model") }}
+                       </td>
+                       <td class="value">
+                                {{ profile.cpu.model_string or _("Not available") }}
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Core count") }}
+                       </td>
+                       <td class="value">
+                                {{ profile.cpu.count }}
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Speed") }}
+                       </td>
+                       <td class="value">
+                               {{ profile.cpu.speed }} MHz (Bogomips: {{ profile.cpu.bogomips }})
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Supported features") }}
+                       </td>
+                       <td class="value">
+                               <table class="cpufeatures">
+                                       <tr>
+                                               {% if profile.cpu.capable_64bit %}
+                                                       <td class="enabled">
+                                               {% else %}
+                                                       <td class="disabled">
+                                               {% end %}
+                                                       {{ _("64 bit")  }}
+                                               <td>
+                                               
+                                               {% if profile.cpu.capable_pae %}
+                                                       <td class="enabled">
+                                               {% else %}
+                                                       <td class="disabled">
+                                               {% end %}
+                                                       {{ _("PAE")  }}
+                                               <td>
+
+                                               {% if profile.cpu.capable_virt %}
+                                                       <td class="enabled">
+                                               {% else %}
+                                                       <td class="disabled">
+                                               {% end %}
+                                                       {{ _("VT-x/AMD-V")  }}
+                                               <td>
+                                       </tr>
+                               </table>
+                       </td>
+               </tr>
+               <tr>
+                       <td colspan="2">
+                               <strong>{{ _("Memory") }}<strong>
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Size") }}
+                       </td>
+                       <td class="value">
+                               {{ format_size(profile.memory) }}
+                       </td>
+               </tr>
+               <tr>
+                       <td colspan="2">
+                               <strong>{{ _("Disk") }}<strong>
+                       </td>
+               </tr>
+               <tr>
+                       <td class="key">
+                               {{ _("Size") }}
+                       </td>
+                       <td class="value">
+                               {{ format_size(profile.root_size) }}
+                       </td>
+               </tr>
+               <tr>
+                       <td colspan="2">
+                               {{ modules.StasyDeviceTable(profile.devices) }}
+                       </td>
+               </tr>
+       </table>
+       <br class="clear" />
+
+       <h3>{{ _("Signature images") }}</h3>
+       <table>
+               {% for i in range(1) %}
+                       <tr>
+                               <td>
+                                       <!-- XXX need some bbcode here -->
+                                       <a href="http://i-use.ipfire.org/profile/{{ profile.public_id }}/{{ i }}.png">
+                                               <img src="http://i-use.ipfire.org/profile/{{ profile.public_id }}/{{ i }}.png"
+                                                       alt="{{ _("Signature image") }}" />
+                                       </a>
+                               </td>
+                       </tr>
+               {% end %}
+       </table>
+
+       <p class="links">
+               {{ _("Last update") }}: {{ locale.format_date(profile.updated) }}
+       </p>
+{% end block %}
index a3b4133fd3a07c901e59c32403eea8a386bbce3a..d1c8f5a554b3147c97d7dd5663fd78d8670e5af4 100644 (file)
 {% extends "stasy-base-1.html" %}
 
-{% block title %}{{ _("Profile") }} {{ profile.public_id }}{% end block %}
+{% block title %}{{ _("Profile lookup") }}{% end block %}
 
 {% block content %}
-       <h3>{{ _("Profile") }} {{ profile.public_id }}</h3>
+       <h3>{{ _("Profile lookup") }}</h3>
 
-       <table>
-               <tr>
-                       <td colspan="2">
-                               <strong>{{ _("Operating system") }}</strong>
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Version") }}
-                       </td>
-                       <td>
-                               {{ profile.release }}
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Architecture") }}
-                       </td>
-                       <td>
-                               {{ profile.cpu.arch }}
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Kernel version") }}
-                       </td>
-                       <td>
-                               {{ profile.kernel }}
-                       </td>
-               </tr>
-       </table>
-
-       {% if profile.hypervisor %}
-               <h4>{{ _("Hypervisor") }}</h4>
-
-               <table>
-                       <tr>
-                               <td>
-                                       {{ _("Vendor") }}
-                               </td>
-                               <td>
-                                       {{ profile.hypervisor.vendor }}
-                               </td>
-                       </tr>
-                       <tr>
-                               <td>
-                                       {{ _("Model") }}
-                               </td>
-                               <td>
-                                       {{ profile.hypervisor.model or _("Unknown") }}
-                               </td>
-                       </tr>
-                       <tr>
-                               <td>
-                                       {{ _("Type") }}
-                               </td>
-                               <td>
-                                       {{ profile.hypervisor.type }}
-                               </td>
-                       </tr>
-               </table>
-       {% end %}
-       
-       <h4>{{ _("Hardware") }}</h4>
-       <table>
-               <tr>
-                       <td colspan="2">
-                               <strong>{{ _("CPU") }}</strong>
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Vendor") }}
-                       </td>
-                       <td>
-                               <a href="/vendor/cpu/{{ profile.cpu.vendor }}">{{ profile.cpu.vendor }}</a>
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Model") }}
-                       </td>
-                       <td>
-                                {{ profile.cpu.model_string or _("Not available") }}
-                       </td>
-               </tr>
-<!--
-               <tr>
-                       <td>
-                               {{ _("CPU flags") }}
-                       </td>
-                       <td>
-                               {{ locale.list(profile.cpu.flags) }}
-                       </td>
-               </tr>
--->
-               <tr>
-                       <td>
-                               {{ _("Supports x86_64") }}
-                       </td>
-                       <td>
-                               {% if profile.cpu.capable_64bit %}
-                                       YES
-                               {% else %}
-                                       NO
-                               {% end %}
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Supports PAE") }}
-                       </td>
-                       <td>
-                               {% if profile.cpu.capable_pae %}
-                                       YES
-                               {% else %}
-                                       NO
-                               {% end %}
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Supports VT-x/AMD-V") }}
-                       </td>
-                       <td>
-                               {% if profile.cpu.capable_virt %}
-                                       YES
-                               {% else %}
-                                       NO
-                               {% end %}
-                       </td>
-               </tr>
-               <tr>
-                       <td colspan="2">
-                               <strong>{{ _("Memory") }}<strong>
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Size") }}
-                       </td>
-                       <td>
-                               {{ int(profile.memory) / 1024 }} {{ _("MegaBytes") }}
-                       </td>
-               </tr>
-               <tr>
-                       <td colspan="2">
-                               <strong>{{ _("Disk") }}<strong>
-                       </td>
-               </tr>
-               <tr>
-                       <td>
-                               {{ _("Size") }}
-                       </td>
-                       <td>
-                               {{ profile.root_size / 1024 }} {{ _("MegaBytes") }}
-                       </td>
-               </tr>
-               <tr>
-                       <td colspan="2">
-                               <strong>{{ _("Devices") }}</strong>
-                       </td>
-               </tr>
-               <tr>
-                       <td colspan="2">
-                               <table>
-                                       {% for device in profile.devices %}
-                                               <tr>
-                                                       <td>
-                                                               {{ device.subsystem.upper() }}
-                                                       </td>
-                                                       <td>
-                                                               <a href="/vendor/{{ device.subsystem.lower() }}/{{ device.vendor }}">{{ device.vendor_string }}</a>
-                                                       </td>
-                                                       <td>
-                                                               <a href="/model/pci/{{ device.vendor }}/{{device.model }}">{{ device.model_string }}</a>
-                                                       </td>
-                                                       <td>
-                                                               {{ device.driver or "&nbsp;" }}
-                                                       </td>
-                                               </tr>
-                                       {% end %}
-                               </table>
-                       </td>
-               </tr>
-       </table>
-
-       <p class="links">
-               {{ _("Last update") }}: {{ locale.format_date(profile.updated) }}
+       <p>
+               Please enter your profile ID and have a look of the hardware components
+               that are installed on your system:
        </p>
+
+       <form name="profile">
+               <p>
+                       <input type="text" name="profile_id" size="40" maxlength="40" />
+                       <input type="submit" value="{{ _("Show profile") }}" />
+               </p>
+       </form>
 {% end block %}
diff --git a/www/templates/stasy-stats-cpu-flags.html b/www/templates/stasy-stats-cpu-flags.html
new file mode 100644 (file)
index 0000000..41771ac
--- /dev/null
@@ -0,0 +1,25 @@
+{% extends "stasy-base-2.html" %}
+
+{% block title %}{% end block %}
+
+{% block content %}
+       <h3>{{ _("CPU flags") }}</h3>
+       <p>
+               On this page we will examine which CPUs provide a specific feature.
+               These charts will help the IPFire developers to make decisions about
+               the operating system configuration.
+       </p>
+
+       <h4>{{ _("CPUs that support long mode") }}</h4>
+       <p>
+               For future planning it is important to know if there is enough hardware
+               support for a 64bit version of IPFire.
+       </p>
+       {{ modules.StasyTable(cpus_lm, sortby="percentage") }}
+
+       <h4>{{ _("CPUs with PAE") }}</h4>
+       <p>
+               This chart shows us which CPUs have got the PAE flag.
+       </p>
+       {{ modules.StasyTable(cpus_pae, sortby="percentage") }}
+{% end block %}
index da8a795c5d5bdc5cf59d9819ace38f7dd36f9c24..435eec0e02b827a541e90e10d66f25b18bdba7fe 100644 (file)
@@ -1,7 +1,23 @@
-{% extends "stasy-base-1.html" %}
+{% extends "stasy-base-2.html" %}
 
 {% block title %}{% end block %}
 
 {% block content %}
-       {{ modules.StasyTable(cpu_vendors) }}
+       <h3>{{ _("CPU vendors") }}</h3>
+       <p>
+               This chart shows us which vendors is the most favorite for the CPUs
+               used in IPFire.
+       </p>
+       {{ modules.StasyTable(cpu_vendors, sortby="percentage") }}
+
+       <h3>{{ _("Speed") }}</h3>
+       <p>
+               The average speed of all systems in the database is:
+               <strong>{{ "%.2f" % average_speed }} MHz</strong>.
+       </p>
+       {{ modules.StasyTable(cpu_speeds) }}
+
+       <p class="links">
+               <a href="cpuflags">See statistics about common CPU flags</a>
+       </p>
 {% end block %}
diff --git a/www/templates/stasy-stats-geo.html b/www/templates/stasy-stats-geo.html
new file mode 100644 (file)
index 0000000..ca9d8aa
--- /dev/null
@@ -0,0 +1,19 @@
+{% extends "stasy-base-2.html" %}
+
+{% block title %}{% end block %}
+
+{% block content %}
+       <h3>{{ _("Language selection") }}</h3>
+       <p>
+               This will give a short overview about what languages are configured
+               on the IPFire webinterface.
+       </p>
+       {{ modules.StasyTable(languages, sortby="percentage") }}
+
+
+       <h3>{{ _("Geo locations") }}</h3>
+       <p>
+               This chart shows us in which country IPFire is running.
+       </p>
+       {{ modules.StasyTable(geo_locations, sortby="percentage") }}
+{% end block %}
diff --git a/www/templates/stasy-stats-memory.html b/www/templates/stasy-stats-memory.html
new file mode 100644 (file)
index 0000000..4c46b68
--- /dev/null
@@ -0,0 +1,12 @@
+{% extends "stasy-base-2.html" %}
+
+{% block title %}{% end block %}
+
+{% block content %}
+       <h3>{{ _("Memory") }}</h3>
+       <p>
+               The average amount of memory of all systems in the database is:
+               <strong>{{ "%.2f" % average_memory }} MB</strong>.
+       </p>
+       {{ modules.StasyTable(memory) }}
+{% end block %}
diff --git a/www/templates/stasy-stats-oses.html b/www/templates/stasy-stats-oses.html
new file mode 100644 (file)
index 0000000..49c5c82
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends "stasy-base-2.html" %}
+
+{% block title %}{% end block %}
+
+{% block content %}
+       <h3>{{ _("Releases") }}</h3>
+       {{ modules.StasyTable(releases, sortby="percentage") }}
+
+       <h3>{{ _("Architectures") }}</h3>
+       {{ modules.StasyTable(arches, sortby="percentage") }}
+
+       <h3>{{ _("Kernels") }}</h3>
+       <p>
+               This chart gives an overview about what kernel the machines are
+               runnning.
+       </p>
+       {{ modules.StasyTable(kernels, sortby="percentage") }}
+{% end block %}
index c4442dc5db47e25186fd91335e78f10a608af4dc..c3f64a26ba92082b4c4df452a4dcbf51bd39d82a 100644 (file)
@@ -1,9 +1,21 @@
-{% extends "stasy-base-1.html" %}
+{% extends "stasy-base-2.html" %}
 
 {% block title %}{% end block %}
 
 {% block content %}
-       {{ modules.StasyTable(is_virtual) }}
-       
-       {{ modules.StasyTable(hypervisor_vendors) }}
+       <h3>{{ _("Virtualization support") }}</h3>
+       <p>
+               IPFire is running very well in a virtual environment.
+       </p>
+       <p>
+               See this chart to get a clue about how many machines are running
+               virtually.
+       </p>
+       {{ modules.StasyTable(is_virtual, sortby="percentage") }}
+
+       <h4>{{ _("Hypervisors") }}</h4>
+       <p>
+               This is a list of all hypervisor vendors that IPFire is running on.
+       </p>
+       {{ modules.StasyTable(hypervisor_vendors, sortby="percentage") }}
 {% end block %}
index 8d0580e8358c998b628c183d11ceba3534751bed..14b1b82b6a59480094e5b0dc884d98363b6339a1 100644 (file)
@@ -1,2 +1,22 @@
-{% extends "stasy-base-1.html" %}
+{% extends "stasy-base-2.html" %}
 
+{% block content %}
+       <h3>{{ _("Statistical accounting") }}</h3>
+       <p>
+               At this place you can find several charts to find out in what
+               specific way the hardware that is used for IPFire is ... XXX.
+       </p>
+
+       <table>
+               <tr>
+                       <td><a href="/stats/cpus">{{ _("CPUs") }}</a></td>
+                       <td><a href="/stats/cpuflags">{{ _("CPU flags") }}</a></td>
+                       <td><a href="/stats/memory">{{ _("Memory") }}</a></td>
+               </tr>
+               <tr>
+                       <td><a href="/stats/oses">{{ _("IPFire versions") }}</a></td>
+                       <td><a href="/stats/virtual">{{ _("Virtualization") }}</a></td>
+                       <td><a href="/stats/geo">{{ _("Geographical information") }}</a></td>
+               </tr>
+       </table>
+{% end block %}
diff --git a/www/templates/stasy-vendor-detail.html b/www/templates/stasy-vendor-detail.html
new file mode 100644 (file)
index 0000000..1bef184
--- /dev/null
@@ -0,0 +1,11 @@
+{% extends "stasy-base-1.html" %}
+
+{% block content %}
+       <h3>{{ vendor_name }}</h3>
+       <p>
+               This is a list of all devices this database knows about from
+               <strong>{{ vendor_name }}</strong>.
+       </p>
+
+       {{ modules.StasyDeviceTable(models) }}
+{% end block %}
index 40eac14aa4916d31cce9711523c3cd1e5ba6d215..8b40a68dbaa387452e0ed7d246b5912847830be4 100644 (file)
@@ -39,6 +39,7 @@ class Application(tornado.web.Application):
                                "SidebarBanner"  : SidebarBannerModule,
                                "SidebarRelease" : SidebarReleaseModule,
                                "StasyTable"     : StasyTableModule,
+                               "StasyDeviceTable" : StasyDeviceTableModule,
                                "TrackerPeerList": TrackerPeerListModule,
                        },
                        xsrf_cookies = True,
@@ -116,11 +117,20 @@ class Application(tornado.web.Application):
                ] + static_handlers)
 
                # stasy.ipfire.org
-               self.add_handlers(r"stasy\.ipfire\.org", [
+               self.add_handlers(r"(fireinfo|stasy)\.ipfire\.org", [
                        (r"/", StasyIndexHandler),
-                       (r"/profile/([a-z0-9]{40})", StasyProfileHandler),
-                       (r"/statistics/cpu", StasyStatsCPUHandler),
-                       (r"/statistics/virtual", StasyStatsVirtualHandler),
+                       (r"/profile/([a-z0-9]{40})", StasyProfileDetailHandler),
+                       (r"/vendor/(pci|usb)/([0-9a-f]{4})", StasyStatsVendorDetail),
+                       (r"/model/(pci|usb)/([0-9a-f]{4})/([0-9a-f]{4})", StasyStatsModelDetail),
+
+                       # Stats handlers                        
+                       (r"/stats", StasyStatsHandler),
+                       (r"/stats/cpus", StasyStatsCPUHandler),
+                       (r"/stats/cpuflags", StasyStatsCPUFlagsHandler),
+                       (r"/stats/geo", StasyStatsGeoHandler),
+                       (r"/stats/memory", StasyStatsMemoryHandler),
+                       (r"/stats/oses", StasyStatsOSesHandler),
+                       (r"/stats/virtual", StasyStatsVirtualHandler),
                ] + static_handlers)
 
                # i-use.ipfire.org
index 6f13a980686942e9a3a1d22e13025be71a952b44..26a241bcf0fa366636091998e897b5a6219cc161 100644 (file)
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+from __future__ import division
+
 import hwdata
 import logging
 import pymongo
@@ -9,6 +11,7 @@ from misc import Singleton
 DATABASE_HOST = ["irma.ipfire.org", "madeye.ipfire.org"]
 DATABASE_NAME = "stasy"
 
+CPU_SPEED_CONSTRAINTS = (0, 500, 1000, 1500, 2000, 2500, 3000, 3500)
 MEMORY_CONSTRAINTS = (0, 64, 128, 256, 512, 1024, 2048, 4096, 8128, 16384)
 
 class ProfileDict(object):
@@ -25,6 +28,14 @@ class ProfileCPU(ProfileDict):
        def vendor(self):
                return self._data.get("vendor")
 
+       @property
+       def speed(self):
+               return self._data.get("speed")
+
+       @property
+       def bogomips(self):
+               return self._data.get("bogomips")
+
        @property
        def model(self):
                return self._data.get("model")
@@ -37,6 +48,10 @@ class ProfileCPU(ProfileDict):
        def flags(self):
                return self._data.get("flags")
 
+       @property
+       def count(self):
+               return self._data.get("count")
+
        @property
        def capable_64bit(self):
                return "lm" in self.flags
@@ -58,10 +73,6 @@ class ProfileHypervisor(ProfileDict):
        def vendor(self):
                return self._data.get("vendor")
 
-       @property
-       def model(self):
-               return self._data.get("model")
-
        @property
        def type(self):
                return self._data.get("type")
@@ -73,6 +84,57 @@ class ProfileDevice(ProfileDict):
                "usb" : hwdata.USB,
        }
 
+       classid2name = {
+               "pci" : {
+                       "00" : "unclassified",
+                       "01" : "Mass storage",
+                       "02" : "Network",
+                       "03" : "Display",
+                       "04" : "Multimedia",
+                       "05" : "Memory controller",
+                       "06" : "Bridge",
+                       "07" : "Communication",
+                       "08" : "Generic system peripheral",
+                       "09" : "Input device",
+                       "0a" : "Docking station",
+                       "0b" : "Processor",
+                       "0c" : "Serial bus",
+                       "0d" : "Wireless",
+                       "0e" : "Intelligent controller",
+                       "0f" : "Satellite communications controller",
+                       "10" : "Encryption controller",
+                       "11" : "Signal processing controller",
+                       "ff" : "Unassigned class",
+               },
+               
+               "usb" : {
+                       "00" : "???",
+                       "01" : "Multimedia",
+                       "02" : "Communication",
+                       "03" : "Input device",
+                       "05" : "Physical ???",
+                       "06" : "Image ???",
+                       "07" : "Printer",
+                       "08" : "Mass storage",
+                       "09" : "Hub",
+                       "0a" : "CDC-Data ???",
+                       "0b" : "Smart card ???",
+                       "0d" : "Content Security ???",
+                       "0e" : "Display",
+                       "0f" : "Personal healthcare ???",
+                       "dc" : "Diagnostic Device",
+                       "e0" : "Wireless",
+                       "ef" : "Misc",
+                       "fe" : "Application specific ???",
+                       "ff" : "Vendor specific ???",
+               }
+       }
+
+       def __cmp__(self, other):
+               return cmp(self.vendor, other.vendor) or \
+                       cmp(self.model, other.model) or \
+                       cmp(self.driver, other.driver)
+
        @property
        def model(self):
                return self._data.get("model")
@@ -101,13 +163,32 @@ class ProfileDevice(ProfileDict):
        def driver(self):
                return self._data.get("driver")
 
+       @property
+       def cls(self):
+               classid = self._data.get("deviceclass")
+
+               if self.subsystem == "pci":
+                       classid = classid[:-4]
+                       if len(classid) == 1:
+                               classid = "0%s" % classid
+
+               elif self.subsystem == "usb" and classid:
+                       classid = classid.split("/")[0]
+                       classid = "%02x" % int(classid)
+
+               try:
+                       return self.classid2name[self.subsystem][classid]
+               except KeyError:
+                       return "N/A"
+
 
 class Profile(ProfileDict):
-       def __init__(self, public_id, updated, data):
-               ProfileDict.__init__(self, data)
+       def __init__(self, profile_blob):
+               ProfileDict.__init__(self, profile_blob.get("profile"))
 
-               self.public_id = public_id
-               self.updated = updated
+               self.public_id = profile_blob.get("public_id")
+               self.updated = profile_blob.get("updated")
+               self._geoip = profile_blob.get("geoip", None)
 
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.public_id)
@@ -122,7 +203,7 @@ class Profile(ProfileDict):
                for d in self._data.get("devices"):
                        d = ProfileDevice(d)
 
-                       if d.driver in ("usb", "hub"):
+                       if d.driver in ("pcieport", "usb", "hub"):
                                continue
 
                        devices.append(d)
@@ -156,8 +237,22 @@ class Profile(ProfileDict):
 
        @property
        def root_size(self):
-               return self.system.get("root_size")
+               return self.system.get("root_size") or 0
+
+       @property
+       def vendor(self):
+               return self.system.get("vendor")
+
+       @property
+       def model(self):
+               return self.system.get("model")
+
+       @property
+       def country_code(self):
+               if self._geoip:
+                       return self._geoip["country_code"].lower()
 
+               return "unknown"
 
 
 class Stasy(object):
@@ -181,10 +276,13 @@ class Stasy(object):
        #
        #       return c
 
+       def profile_exists(self, public_id):
+               return self._db.profiles.find({ "public_id" : public_id }).count() >= 1
+
        def get_profile(self, public_id):
                # XXX should only find one object in the end
                for p in self._db.profiles.find({ "public_id" : public_id }):
-                       p = Profile(p.get("public_id"), p.get("updated"), p.get("profile"))
+                       p = Profile(p)
 
                return p
 
@@ -210,7 +308,7 @@ class Stasy(object):
                return self._db.profiles.distinct("profile.cpu.vendor")
 
        @property
-       def cpu_map(self):
+       def cpu_vendors_map(self):
                cpus = {}
 
                for vendor in self.cpu_vendors:
@@ -222,7 +320,36 @@ class Stasy(object):
                return cpus
 
        @property
-       def memory_map(self):
+       def cpu_speed_average(self):
+               speed = 0
+
+               all = self._db.profiles.find()
+
+               # XXX ugly. needs to be done by group()
+               for m in all:
+                       if not m.has_key("profile"):
+                               continue
+                       speed += m.get("profile").get("cpu").get("speed")
+
+               return (speed / all.count())
+
+       @property
+       def cpu_speed_map(self):
+               cpu_speeds = {}
+
+               for i in range(len(CPU_SPEED_CONSTRAINTS) - 1):
+                       min, max = CPU_SPEED_CONSTRAINTS[i:i+2]
+
+                       cpu_speeds[min, max] = \
+                               self._db.profiles.find(
+                                       { "profile.cpu.speed" : {
+                                               "$gte" : min, "$lt" : max
+                                       }
+                               }).count()
+
+               return cpu_speeds
+
+       def get_memory_map(self):
                memory = {}
 
                for i in range(len(MEMORY_CONSTRAINTS) - 1):
@@ -288,6 +415,17 @@ class Stasy(object):
        def languages(self):
                return self._db.profiles.distinct("profile.system.language")
 
+       def get_language_map(self):
+               languages = {}
+
+               for language in self.languages:
+                       languages[language] = \
+                               self._db.profiles.find({
+                                       "profile.system.language" : language
+                               }).count()
+
+               return languages
+
        @property
        def vendors(self):
                return self._db.profiles.distinct("profile.system.vendor")
@@ -308,6 +446,141 @@ class Stasy(object):
        def models(self):
                return self._db.profiles.distinct("profile.system.model")
 
+       @property
+       def model_map(self):
+               models = {}
+
+               for model in self.models:
+                       models[model] = \
+                               self._db.profiles.find({
+                                       "profile.system.model" : model
+                               }).count()
+
+               return models
+
+       @property
+       def arches(self):
+               return self._db.profiles.distinct("profile.cpu.arch")
+
+       @property
+       def arch_map(self):
+               arches = {}
+
+               for arch in self.arches:
+                       arches[arch] = \
+                               self._db.profiles.find({
+                                       "profile.cpu.arch" : arch
+                               }).count()
+
+               return arches
+
+       @property
+       def kernels(self):
+               return self._db.profiles.distinct("profile.system.kernel_release")
+
+       @property
+       def kernel_map(self):
+               kernels = {}
+
+               for kernel in self.kernels:
+                       kernels[kernel] = \
+                               self._db.profiles.find({
+                                       "profile.system.kernel_release" : kernel
+                               }).count()
+
+               return kernels
+
+       @property
+       def releases(self):
+               return self._db.profiles.distinct("profile.system.release")
+
+       @property
+       def release_map(self):
+               releases = {}
+
+               for release in self.releases:
+                       releases[release] = \
+                               self._db.profiles.find({
+                                       "profile.system.release" : release
+                               }).count()
+
+               return releases
+
+       def get_device_percentage(self, bus, vendor_id, model_id):
+               profiles_with_device = self._db.profiles.find({
+                       "profile.devices.subsystem" : bus,
+                       "profile.devices.vendor" : vendor_id,
+                       "profile.devices.model" : model_id,
+               })
+
+               # XXX must only be systems that have profile data
+               profiles_all = self._db.profiles.find()
+
+               if not profiles_all.count():
+                       return 0
+
+               return profiles_with_device.count() / profiles_all.count()
+
+       def get_cpu_flag_map(self, flag):
+               flags = {}
+
+               flags[True] = \
+                       self._db.profiles.find({
+                               "profile.cpu.flags" : flag,
+                       }).count()
+
+               # XXX must only be systems that have profile data
+               profiles_all = self._db.profiles.find()
+
+               flags[False] = profiles_all.count() - flags[True]
+
+               return flags
+
+       @property
+       def geo_locations(self):
+               return [code.lower() for code in self._db.profiles.distinct("geoip.country_code")]
+
+       def get_geo_location_map(self):
+               geo_locations = {}
+
+               count = 0
+               for geo_location in self.geo_locations:
+                       geo_locations[geo_location] = \
+                               self._db.profiles.find({
+                                       "geoip.country_code" : geo_location
+                               }).count()
+
+                       count += geo_locations[geo_location]
+
+               # XXX must only be systems that have profile data
+               profiles_all = self._db.profiles.find()
+
+               unknown_count = profiles_all.count() - count
+               if unknown_count:
+                       geo_locations["unknown"] = unknown_count
+
+               return geo_locations
+
+       def get_models_by_vendor(self, subsystem, vendor_id):
+               devices = []
+
+               # XXX must only be systems that have profile data
+               profiles_all = self._db.profiles.find()
+
+               for profile in profiles_all:
+                       if not profile.has_key("profile"):
+                               continue
+
+                       profile = Profile(profile)
+
+                       for device in profile.devices:
+                               if not device.vendor == vendor_id:
+                                       continue
+
+                               if not device in devices:
+                                       devices.append(device)
+
+               return devices
 
 
 if __name__ == "__main__":
@@ -327,11 +600,20 @@ if __name__ == "__main__":
        print s.hypervisor_vendors
        print s.hypervisor_models
        print s.languages
+       print s.language_maps
        print s.vendors
        print s.vendor_map
        print s.models
        print s.cpus
+       print s.cpu_map
+       print s.arches
+       print s.arch_map
+       print s.kernels
+       print s.kernel_map
+       print s.releases
+       print s.release_map
 
        p = s.get_profile("0b5f4fe2162fdfbfa29b632610e317078fa70d34")
-       print p
-       print p.hypervisor
+       print p._data
+#      print p.hypervisor
+
index 7b8c506faedf85f44f0658fd615e2089d53bc29f..6a66ee77bc574f914942b4e3e6659191233f4270 100644 (file)
@@ -1,5 +1,8 @@
 #!/usr/bin/python
 
+from __future__ import division
+
+import hwdata
 import tornado.web
 
 import backend
@@ -11,6 +14,30 @@ class StasyBaseHandler(BaseHandler):
        def stasy(self):
                return backend.Stasy()
 
+       def format_size(self, s):
+               units = ("K", "M", "G", "T")
+               unit = 0
+
+               while s >= 1024 and unit < len(units):
+                       s /= 1024
+                       unit += 1
+
+               return "%.1f%s" % (s, units[unit])
+
+       def cut_string(self, s, limit=15):
+               if len(s) > limit:
+                       s = s[:limit] + "..."
+
+               return s
+
+       def render(self, *args, **kwargs):
+               kwargs.update({
+                       "cut_string" : self.cut_string,
+                       "format_size" : self.format_size,
+               })
+
+               return BaseHandler.render(self, *args, **kwargs)
+
 
 class StasyIndexHandler(StasyBaseHandler):
        def get(self):
@@ -18,20 +45,61 @@ class StasyIndexHandler(StasyBaseHandler):
 
                self.render("stasy-index.html", profiles=profiles)
 
+       def post(self):
+               profile_id = self.get_argument("profile_id", None)
+               if not profile_id:
+                       raise tornado.web.HTTPError(400, "No profile ID was given.")
+
+               if not self.stasy.profile_exists(profile_id):
+                       raise tornado.web.HTTPError(404, "Profile does not exist.")
 
-class StasyProfileHandler(StasyBaseHandler):
+               self.redirect("/profile/%s" % profile_id)
+
+
+class StasyProfileDetailHandler(StasyBaseHandler):
        def get(self, profile_id):
                profile = self.stasy.get_profile(profile_id)
                if not profile:
                        raise tornado.web.HTTPError(404, "Profile not found: %s" % profile_id)
 
-               self.render("stasy-profile.html", profile=profile)
+               self.render("stasy-profile-detail.html", profile=profile)
+
+
+class StasyStatsHandler(StasyBaseHandler):
+       def get(self):
+               self.render("stasy-stats.html")
 
 
 class StasyStatsCPUHandler(StasyBaseHandler):
        def get(self):
                return self.render("stasy-stats-cpus.html",
-                       cpu_vendors = self.stasy.cpu_map)
+                       cpu_vendors=self.stasy.cpu_vendors_map,
+                       average_speed=self.stasy.cpu_speed_average,
+                       cpu_speeds=self.stasy.cpu_speed_map)
+
+
+class StasyStatsCPUFlagsHandler(StasyBaseHandler):
+       def get(self):
+               kwargs = {}
+
+               for flag in ("lm", "pae"):
+                       kwargs["cpus_" + flag] = self.stasy.get_cpu_flag_map(flag)
+
+               return self.render("stasy-stats-cpu-flags.html", **kwargs)
+
+class StasyStatsMemoryHandler(StasyBaseHandler):
+       def get(self):
+               return self.render("stasy-stats-memory.html",
+                       average_memory=self.stasy.memory_average,
+                       memory=self.stasy.get_memory_map())
+
+
+class StasyStatsOSesHandler(StasyBaseHandler):
+       def get(self):
+               return self.render("stasy-stats-oses.html",
+                       arches=self.stasy.arch_map,
+                       kernels=self.stasy.kernel_map,
+                       releases=self.stasy.release_map)
 
 
 class StasyStatsVirtualHandler(StasyBaseHandler):
@@ -39,3 +107,49 @@ class StasyStatsVirtualHandler(StasyBaseHandler):
                return self.render("stasy-stats-virtual.html",
                        hypervisor_vendors = self.stasy.hypervisor_map,
                        is_virtual = self.stasy.virtual_map)
+
+class StasyStatsGeoHandler(StasyBaseHandler):
+       def get(self):
+               return self.render("stasy-stats-geo.html",
+                       languages = self.stasy.get_language_map(),
+                       geo_locations = self.stasy.get_geo_location_map())
+
+
+class StasyStatsVendorDetail(StasyBaseHandler):
+       def get(self, bus, vendor_id):
+               # XXX some way ugly
+               bus2cls = {
+                       "pci" : hwdata.PCI,
+                       "usb" : hwdata.USB
+               }
+               cls = bus2cls[bus.lower()]
+               vendor_name = cls().get_vendor(vendor_id)
+
+               # Get a list of all models we know from this vendor
+               models = self.stasy.get_models_by_vendor(bus, vendor_id)
+
+               self.render("stasy-vendor-detail.html",
+                       vendor_name=vendor_name, models=models)
+
+
+class StasyStatsModelDetail(StasyBaseHandler):
+       def get(self, bus, vendor_id, model_id):
+               bus2cls = {
+                       "pci" : hwdata.PCI,
+                       "usb" : hwdata.USB
+               }
+               
+               cls = bus2cls[bus.lower()]
+
+               vendor_name = cls().get_vendor(vendor_id)
+               model_name = cls().get_device(vendor_id, model_id)
+               
+               percentage = \
+                       self.stasy.get_device_percentage(bus, vendor_id, model_id) * 100
+
+               self.render("stasy-model-detail.html",
+                       vendor_id=vendor_id,
+                       vendor_name=vendor_name,
+                       model_id=model_id,
+                       model_name=model_name,
+                       percentage=percentage)
index 0ed5ccf31448ee1218f431d5126c3619fba205f7..fab4a3a62f5bb23e157a503ab4c0860557aeab6f 100644 (file)
@@ -1,6 +1,9 @@
 #!/usr/bin/python
 
+from __future__ import division
+
 import logging
+import operator
 import socket
 import textile
 import tornado.escape
@@ -9,6 +12,7 @@ import tornado.web
 from tornado.database import Row
 
 import backend
+import backend.stasy
 
 class UIModule(tornado.web.UIModule):
        @property
@@ -125,18 +129,40 @@ class TrackerPeerListModule(UIModule):
 
 
 class StasyTableModule(UIModule):
-       def render(self, items):
+       def render(self, items, sortby="key", reverse=False):
                hundred_percent = 0
                for v in items.values():
                        hundred_percent += v
 
+               keys = []
+               if sortby == "key":
+                       keys = sorted(items.keys(), reverse=reverse)
+               elif sortby == "percentage":
+                       keys = [k for k,v in sorted(items.items(), key=operator.itemgetter(1))]
+                       if not reverse:
+                               keys = reversed(keys)
+               else:
+                       raise Exception, "Unknown sortby parameter was provided"
+
                if hundred_percent:
                        _items = []
-                       for k in sorted(items.keys()):
-                               v = float(items[k] * 100) / hundred_percent
+                       for k in keys:
+                               v = items[k] * 100 / hundred_percent
                                _items.append((k, v))
                        items = _items
 
-               print items
-
                return self.render_string("modules/stasy-table.html", items=items)
+
+
+class StasyDeviceTableModule(UIModule):
+       def render(self, devices):
+               groups = {}
+
+               for device in devices:
+                       if not groups.has_key(device.cls):
+                               groups[device.cls] = []
+
+                       groups[device.cls].append(device)
+               
+               return self.render_string("modules/stasy-table-devices.html",
+                       groups=groups.items())