Add application files.
This commit is contained in:
parent
a705608605
commit
2e89a950f1
9 changed files with 916 additions and 0 deletions
126
saltinventory.py
Executable file
126
saltinventory.py
Executable file
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
#
|
||||
# Requirements:
|
||||
#
|
||||
# * salt
|
||||
# * python-netaddr
|
||||
#
|
||||
|
||||
import salt.client
|
||||
from netaddr import *
|
||||
from jinja2 import Template, BaseLoader, TemplateNotFound, FileSystemLoader, Environment
|
||||
|
||||
# settings
|
||||
|
||||
config = {
|
||||
'base_path': 'www/',
|
||||
'host_page_path': 'www/hosts/',
|
||||
'network_page_path': 'www/networks/',
|
||||
'template_path': 'templates/',
|
||||
}
|
||||
|
||||
# class definitions
|
||||
|
||||
class Inventorizer:
|
||||
config = None
|
||||
|
||||
saltcmd = None
|
||||
|
||||
host_list = {}
|
||||
network_list = {}
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.saltcmd = salt.client.LocalClient()
|
||||
self.process()
|
||||
|
||||
def process(self):
|
||||
self.collectHostData()
|
||||
self.createHostList()
|
||||
self.collectNetworkData()
|
||||
self.createHostPages()
|
||||
self.createNetworkList()
|
||||
self.createNetworkPages()
|
||||
|
||||
def collectHostData(self):
|
||||
self.host_list = self.saltcmd.cmd('*', 'grains.items')
|
||||
|
||||
def collectNetworkData(self):
|
||||
result = self.saltcmd.cmd('*', 'network.interfaces')
|
||||
for hostname, hostdata in result.iteritems():
|
||||
for interface, interfacedata in hostdata.iteritems():
|
||||
|
||||
if interfacedata.has_key('inet'):
|
||||
for netdata in interfacedata['inet']:
|
||||
ip = IPNetwork(netdata['address'] + '/' + netdata['netmask'])
|
||||
host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip}
|
||||
self.addNetworkHost(ip, host)
|
||||
|
||||
if interfacedata.has_key('secondary'):
|
||||
for netdata in interfacedata['secondary']:
|
||||
ip = IPNetwork(netdata['address'] + '/' + netdata['netmask'])
|
||||
host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip}
|
||||
self.addNetworkHost(ip, host)
|
||||
|
||||
if interfacedata.has_key('inet6'):
|
||||
for netdata in interfacedata['inet6']:
|
||||
ip = IPNetwork(netdata['address'] + '/' + netdata['prefixlen'])
|
||||
host = {'hostname': hostname, 'address': netdata['address'], 'netobj': ip}
|
||||
self.addNetworkHost(ip, host)
|
||||
|
||||
|
||||
def addNetworkHost(self, ip, host):
|
||||
if self.network_list.has_key(ip.cidr):
|
||||
self.network_list[ip.cidr].append(host)
|
||||
else:
|
||||
self.network_list[ip.cidr] = [host]
|
||||
|
||||
def createHostList(self):
|
||||
fo = open(self.config['base_path'] + "hostlist.html", "wb")
|
||||
env = Environment(loader = FileSystemLoader(config['template_path']))
|
||||
template = env.get_template('hostlist_template.html')
|
||||
fo.write(template.render({'hostlist': self.host_list}))
|
||||
fo.close
|
||||
|
||||
def createHostPages(self):
|
||||
for hostname, hostdata in self.host_list.iteritems():
|
||||
self.createHostPage(hostname, hostdata)
|
||||
|
||||
def createHostPage(self, hostname, hostdata):
|
||||
fo = open(self.config['host_page_path'] + hostname + ".html", "wb")
|
||||
env = Environment(loader = FileSystemLoader(config['template_path']))
|
||||
template = env.get_template('host_template.html')
|
||||
fo.write(template.render(hostdata))
|
||||
fo.close
|
||||
|
||||
def createNetworkList(self):
|
||||
fo = open(self.config['base_path'] + "networklist.html", "wb")
|
||||
env = Environment(loader = FileSystemLoader(config['template_path']))
|
||||
env.filters['len'] = self.lenFilter
|
||||
template = env.get_template('networklist_template.html')
|
||||
fo.write(template.render({'networklist': self.network_list}))
|
||||
fo.close
|
||||
|
||||
def createNetworkPages(self):
|
||||
for network, networkdata in self.network_list.iteritems():
|
||||
self.createNetworkPage(network, networkdata)
|
||||
|
||||
def createNetworkPage(self, network, networkdata):
|
||||
fo = open(self.config['network_page_path'] + str(network.cidr).replace('/', '_') + ".html", "wb")
|
||||
env = Environment(loader = FileSystemLoader(config['template_path']))
|
||||
env.filters['replace'] = self.replaceFilter
|
||||
template = env.get_template('network_template.html')
|
||||
fo.write(template.render({'network': network, 'data': networkdata}))
|
||||
fo.close
|
||||
|
||||
def lenFilter(self, list):
|
||||
return len(list)
|
||||
|
||||
def replaceFilter(self, str, old, new):
|
||||
return str(str).replace(old, new)
|
||||
|
||||
# main program
|
||||
|
||||
inv = Inventorizer(config)
|
||||
|
59
templates/host_template.html
Normal file
59
templates/host_template.html
Normal file
|
@ -0,0 +1,59 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>{{ id }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="http://cdn.datatables.net/1.10.4/css/jquery.dataTables.css">
|
||||
<link rel="stylesheet" type="text/css" href="../res/style.css">
|
||||
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="../res/jquery.number.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="http://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>
|
||||
<script type="text/javascript" class="init">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<header class="header clearfix">
|
||||
<div class="logo">Inventory | {{ id }}</div>
|
||||
|
||||
<nav class="menu_main">
|
||||
<ul>
|
||||
<li><a href="../hostlist.html">Hostlist</a></li>
|
||||
<li><a href="../networklist.html">Networks</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
<article class="article clearfix">
|
||||
<h2>System information</h2>
|
||||
<h3>Generic</h3>
|
||||
<ul>
|
||||
<li>Operating System: {{ kernel }} ({{ kernelrelease }})</li>
|
||||
<li>Distribution: {{ lsb_distrib_description }}</li>
|
||||
<li>Machine type: {% if virtual != "physical" %} Virtual ({{ virtual }}) {% else %} Physical {% endif %}</li>
|
||||
<li>Architecture: {{ osarch }}</li>
|
||||
<li>Salt version: {{ saltversion }}</li>
|
||||
</ul>
|
||||
<h3>CPU</h3>
|
||||
<ul>
|
||||
<li>Numer of CPUs: {{ num_cpus }}</li>
|
||||
<li>CPU architecture: {{ cpuarch }}</li>
|
||||
<li>CPU model: {{ cpu_model }}</li>
|
||||
</ul>
|
||||
<h3>Memory</h3>
|
||||
<ul>
|
||||
<li>Total memory: {{ mem_total }}</li>
|
||||
</ul>
|
||||
<h3>Storage</h3>
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<h3>Network</h3>
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
137
templates/hostlist_template.html
Normal file
137
templates/hostlist_template.html
Normal file
|
@ -0,0 +1,137 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Host list</title>
|
||||
<link rel="stylesheet" type="text/css" href="http://cdn.datatables.net/1.10.4/css/jquery.dataTables.css">
|
||||
<link rel="stylesheet" type="text/css" href="res/style.css">
|
||||
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="res/jquery.number.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="http://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>
|
||||
<script type="text/javascript" class="init">
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// search box for each column (http://www.datatables.net/examples/api/multi_filter.html) - Part 1
|
||||
|
||||
$('#hostlist thead td').each( function () {
|
||||
var title = $('#hostlist thead th').eq( $(this).index() ).text();
|
||||
$(this).html( '<input type="text" placeholder="Search '+title+'" />' );
|
||||
} );
|
||||
|
||||
|
||||
var table = $('#hostlist').DataTable({
|
||||
"footerCallback": function ( row, data, start, end, display ) {
|
||||
var api = this.api(), data;
|
||||
|
||||
// Remove the formatting to get integer data for summation
|
||||
var intVal = function ( i ) {
|
||||
return typeof i === 'string' ?
|
||||
i.replace(/[\$,]/g, '')*1 :
|
||||
typeof i === 'number' ?
|
||||
i : 0;
|
||||
};
|
||||
|
||||
// Total over this page
|
||||
// mem
|
||||
memTotal = api
|
||||
.column( 2, { page: 'current'} )
|
||||
.data()
|
||||
.reduce( function (a, b) {
|
||||
return intVal(a) + intVal(b);
|
||||
}, 0 );
|
||||
// cpu
|
||||
cpuTotal = api
|
||||
.column( 3, { page: 'current'} )
|
||||
.data()
|
||||
.reduce( function (a, b) {
|
||||
return intVal(a) + intVal(b);
|
||||
}, 0 );
|
||||
|
||||
// Update footer
|
||||
$( api.column( 2 ).footer() ).html(
|
||||
$.number(memTotal, 0, ",", ".")
|
||||
);
|
||||
|
||||
$( api.column( 3 ).footer() ).html(
|
||||
cpuTotal
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// search box for each column (http://www.datatables.net/examples/api/multi_filter.html) - Part 2
|
||||
|
||||
// Apply the search
|
||||
table.columns().eq( 0 ).each( function ( colIdx ) {
|
||||
$( 'input', table.column( colIdx ).header() ).on( 'keyup change', function () {
|
||||
table
|
||||
.column( colIdx )
|
||||
.search( this.value, true )
|
||||
.draw();
|
||||
} );
|
||||
} );
|
||||
|
||||
$('td.number').number( true, 0, ",", "." );
|
||||
} );
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<header class="header clearfix">
|
||||
<div class="logo">Inventory | Host list</div>
|
||||
|
||||
<nav class="menu_main">
|
||||
<ul>
|
||||
<li class="active"><a href="hostlist.html">Hostlist</a></li>
|
||||
<li><a href="networklist.html">Networks</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
<article class="article clearfix">
|
||||
|
||||
<h2>Hosts</h2>
|
||||
|
||||
|
||||
<table id="hostlist" class="display">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>OS</th>
|
||||
<th>RAM (MB)</th>
|
||||
<th># CPUs</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Name</th>
|
||||
<td>OS</th>
|
||||
<td>RAM</th>
|
||||
<td># CPUs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, data in hostlist.iteritems() %}
|
||||
<tr>
|
||||
<td><a href = 'hosts/{{ name }}.html'>{{ name }}</a></td>
|
||||
<td>{{ data.lsb_distrib_description }}</td>
|
||||
<td class="right number">{{ data.mem_total }}</td>
|
||||
<td class="right">{{ data.num_cpus }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="right"></th>
|
||||
<th class="right"></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</article>
|
||||
|
||||
</body>
|
||||
</html>
|
60
templates/network_template.html
Normal file
60
templates/network_template.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>{{ network.cidr }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="http://cdn.datatables.net/1.10.4/css/jquery.dataTables.css">
|
||||
<link rel="stylesheet" type="text/css" href="../res/style.css">
|
||||
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="../res/jquery.number.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="http://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>
|
||||
<script type="text/javascript" class="init">
|
||||
$(document).ready(function() {
|
||||
var table = $('#adresses').DataTable();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<header class="header clearfix">
|
||||
<div class="logo">Inventory | {{ network.cidr }}</div>
|
||||
|
||||
<nav class="menu_main">
|
||||
<ul>
|
||||
<li><a href="../hostlist.html">Hostlist</a></li>
|
||||
<li><a href="../networklist.html">Networks</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
<article class="article clearfix">
|
||||
<h2>Adresses</h2>
|
||||
<table id="adresses" class="display">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Address</th>
|
||||
<th>Host</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Address</th>
|
||||
<td>Host</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for netdata in data %}
|
||||
<tr>
|
||||
<td>{{ netdata['address'] }}</td>
|
||||
<td><a href="../hosts/{{ netdata['hostname'] }}.html">{{ netdata['hostname'] }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
67
templates/networklist_template.html
Normal file
67
templates/networklist_template.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<title>Network list</title>
|
||||
<link rel="stylesheet" type="text/css" href="http://cdn.datatables.net/1.10.4/css/jquery.dataTables.css">
|
||||
<link rel="stylesheet" type="text/css" href="res/style.css">
|
||||
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="res/jquery.number.min.js"></script>
|
||||
<script type="text/javascript" language="javascript" src="http://cdn.datatables.net/1.10.4/js/jquery.dataTables.min.js"></script>
|
||||
<script type="text/javascript" class="init">
|
||||
|
||||
$(document).ready(function() {
|
||||
var table = $('#networklist').DataTable();
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<header class="header clearfix">
|
||||
<div class="logo">Inventory | Network list</div>
|
||||
|
||||
<nav class="menu_main">
|
||||
<ul>
|
||||
<li><a href="hostlist.html">Hostlist</a></li>
|
||||
<li class="active"><a href="networklist.html">Networks</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
<article class="article clearfix">
|
||||
|
||||
<h2>Networks</h2>
|
||||
|
||||
<table id="networklist" class="display">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network</th>
|
||||
<th>Usage</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Network</th>
|
||||
<td>Usage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for network, data in networklist.iteritems() %}
|
||||
<tr>
|
||||
<td><a href = 'networks/{{ network.cidr|replace('/', '_') }}.html'>{{ network.cidr }}</a></td>
|
||||
<td>{{ data|len }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</article>
|
||||
|
||||
</body>
|
||||
</html>
|
2
www/res/jquery.number.min.js
vendored
Normal file
2
www/res/jquery.number.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
48
www/res/reset.css
Normal file
48
www/res/reset.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* CSS reset. Based on HTML5 boilerplate reset http://html5boilerplate.com/ */
|
||||
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display:block; }
|
||||
audio[controls], canvas, video { display:inline-block; *display:inline; *zoom:1; }
|
||||
html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
|
||||
body { margin: 0; font-size: 14px; line-height: 1.4; }
|
||||
body, button, input, select, textarea { font-family:sans-serif; }
|
||||
a:focus { outline:thin dotted; }
|
||||
a:hover, a:active { outline:0; }
|
||||
abbr[title] { border-bottom:1px dotted; }
|
||||
b, strong { font-weight:bold; }
|
||||
blockquote { margin:1em 40px; }
|
||||
dfn { font-style:italic; }
|
||||
hr { display:block; height:1px; border:0; border-top:1px solid #ccc; margin:1em 0; padding:0; }
|
||||
ins { background:#ff9; color:#000; text-decoration:none; }
|
||||
mark { background:#ff0; color:#000; font-style:italic; font-weight:bold; }
|
||||
pre, code, kbd, samp { font-family:monospace, monospace; _font-family:'courier new', monospace; font-size:1em; }
|
||||
pre { white-space:pre; white-space:pre-wrap; word-wrap:break-word; }
|
||||
q { quotes:none; }
|
||||
q:before, q:after { content:""; content:none; }
|
||||
small { font-size:85%; }
|
||||
sub, sup { font-size:75%; line-height:0; position:relative; vertical-align:baseline; }
|
||||
sup { top:-0.5em; }
|
||||
sub { bottom:-0.25em; }
|
||||
ul, ol { margin:1em 0; padding:0 0 0 2em; }
|
||||
dd { margin:0 0 0 40px; }
|
||||
nav ul, nav ol { list-style:none; margin:0; padding:0; }
|
||||
img { border:0; -ms-interpolation-mode:bicubic; }
|
||||
svg:not(:root) { overflow:hidden;}
|
||||
figure { margin:0; }
|
||||
form { margin:0; }
|
||||
fieldset { border:0; margin:0; padding:0; }
|
||||
legend { border:0; *margin-left:-7px; padding:0; }
|
||||
label { cursor:pointer; }
|
||||
button, input, select, textarea { font-size:100%; margin:0; vertical-align:baseline; *vertical-align:middle; }
|
||||
button, input { line-height:normal; *overflow:visible; }
|
||||
button, input[type="button"], input[type="reset"], input[type="submit"] { cursor:pointer; -webkit-appearance:button; }
|
||||
input[type="checkbox"], input[type="radio"] { box-sizing:border-box; }
|
||||
input[type="search"] { -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; }
|
||||
button::-moz-focus-inner, input::-moz-focus-inner { border:0; padding:0; }
|
||||
textarea { overflow:auto; vertical-align:top; }
|
||||
input:valid, textarea:valid { }
|
||||
input:invalid, textarea:invalid { background-color:#f0dddd; }
|
||||
table { border-collapse:collapse; border-spacing:0; }
|
||||
.hidden { display:none; visibility:hidden; }
|
||||
.clearfix:before, .clearfix:after { content:""; display:table; }
|
||||
.clearfix:after { clear:both; }
|
||||
.clearfix { zoom:1; }
|
||||
/* End CSS reset */
|
255
www/res/skin.css
Normal file
255
www/res/skin.css
Normal file
|
@ -0,0 +1,255 @@
|
|||
/* Skin "iSimple" by Renat Rafikov */
|
||||
body {
|
||||
background:#f2f2f2;
|
||||
font-family:arial, sans-serif;
|
||||
}
|
||||
|
||||
a { color:#0085c5; }
|
||||
a:hover { text-decoration:none; }
|
||||
a:visited { color:#4a00c5; }
|
||||
|
||||
ul li, ol li {
|
||||
padding:0 0 0.4em 0;
|
||||
}
|
||||
|
||||
|
||||
.container {
|
||||
max-width:1300px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin:1px 0 0.5em 0;
|
||||
padding:1.5em 3% 0 3%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
float:left;
|
||||
display:inline-block;
|
||||
font-size:18px;
|
||||
text-shadow:1px 1px 1px #ffffff;
|
||||
}
|
||||
|
||||
.menu_main {
|
||||
width:50%;
|
||||
float:right;
|
||||
text-align:right;
|
||||
margin:0.3em 0 0 0;
|
||||
font-size:12px;
|
||||
}
|
||||
.menu_main a,
|
||||
.menu_main a:hover {
|
||||
color:#0085c5;
|
||||
}
|
||||
.menu_main li {
|
||||
display:inline-block;
|
||||
margin:0 0 0 4px;
|
||||
}
|
||||
.menu_main li.active,
|
||||
.menu_main li.active a {
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding:0 1% 1em 1%;
|
||||
}
|
||||
|
||||
.hero {
|
||||
background:#fff;
|
||||
border:1px solid #fff;
|
||||
-webkit-border-radius:5px;
|
||||
-moz-border-radius:5px;
|
||||
border-radius:5px;
|
||||
-webkit-box-shadow:#8b8b8b 0px 0px 5px inset;
|
||||
-moz-box-shadow:#8b8b8b 0px 0px 5px inset;
|
||||
box-shadow:#8b8b8b 0px 0px 5px inset;
|
||||
padding:15px 0 15px 2%;
|
||||
margin:0 0 15px 0;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size:24px;
|
||||
font-size:18px;
|
||||
color:#3d3d3d;
|
||||
}
|
||||
|
||||
.article {
|
||||
background:#fff;
|
||||
border:1px solid #cbcbcb;
|
||||
-webkit-border-radius:5px;
|
||||
-moz-border-radius:5px;
|
||||
border-radius:5px;
|
||||
-webkit-box-shadow:#8b8b8b 0px 0px 3px;
|
||||
-moz-box-shadow:#8b8b8b 0px 0px 3px;
|
||||
box-shadow:#8b8b8b 0px 0px 3px;
|
||||
padding:15px 0 15px 2%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding:1em 3% 3em 3%;
|
||||
color:#717171;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
width:49%;
|
||||
float:left;
|
||||
text-shadow:1px 1px 1px #ffffff;
|
||||
}
|
||||
|
||||
.menu_bottom {
|
||||
width:50%;
|
||||
float:right;
|
||||
text-align:right;
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-size:12px;
|
||||
}
|
||||
.menu_bottom a,
|
||||
.menu_bottom a:hover {
|
||||
color:#0085c5;
|
||||
}
|
||||
.menu_bottom li {
|
||||
display:inline-block;
|
||||
margin:0 0 0 4px;
|
||||
}
|
||||
.menu_bottom li.active,
|
||||
.menu_bottom li.active a {
|
||||
color:#666;
|
||||
text-decoration:none;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:22px;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
font-weight:normal;
|
||||
}
|
||||
h5, h6 {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.form label {
|
||||
display:inline-block;
|
||||
padding:0 0 4px 0;
|
||||
}
|
||||
|
||||
a.button,
|
||||
.button {
|
||||
border:0;
|
||||
text-align:center;
|
||||
text-decoration:none;
|
||||
-webkit-border-radius:4px;
|
||||
-moz-border-radius:4px;
|
||||
border-radius:4px;
|
||||
-webkit-box-shadow:#999 0px 0px 1px;
|
||||
-moz-box-shadow:#999 0px 0px 1px;
|
||||
box-shadow:#999 0px 0px 1px;
|
||||
background:#4aa6d6;
|
||||
background:-webkit-gradient(linear, 0 0, 0 bottom, from(#1f7daa), to(#4aa6d6));
|
||||
background:-webkit-linear-gradient(#1f7daa, #4aa6d6);
|
||||
background:-moz-linear-gradient(#1f7daa, #4aa6d6);
|
||||
background:-ms-linear-gradient(#1f7daa, #4aa6d6);
|
||||
background:-o-linear-gradient(#1f7daa, #4aa6d6);
|
||||
background:linear-gradient(#1f7daa, #4aa6d6);
|
||||
color:#fff;
|
||||
padding:10px 20px;
|
||||
font-family:verdana, sans-serif;
|
||||
text-shadow:1px 1px 1px #12455d;
|
||||
display:inline-block;
|
||||
}
|
||||
a.button:hover,
|
||||
.button:hover {
|
||||
color:#fff;
|
||||
background:-webkit-gradient(linear, 0 0, 0 bottom, from(#4aa6d6), to(#1f7daa));
|
||||
background:-webkit-linear-gradient(#4aa6d6, #1f7daa);
|
||||
background:-moz-linear-gradient(#4aa6d6, #1f7daa);
|
||||
background:-ms-linear-gradient(#4aa6d6, #1f7daa);
|
||||
background:-o-linear-gradient(#4aa6d6, #1f7daa);
|
||||
background:linear-gradient(#4aa6d6, #1f7daa);
|
||||
}
|
||||
a.button:active,
|
||||
.button:active {
|
||||
color:#093950;
|
||||
text-shadow:1px 1px 1px #7ac8f0;
|
||||
-webkit-box-shadow:#093950 0px 2px 3px inset;
|
||||
-moz-box-shadow:#093950 0px 2px 3px inset;
|
||||
box-shadow:#093950 0px 2px 3px inset;
|
||||
}
|
||||
|
||||
.table {
|
||||
width:100%;
|
||||
}
|
||||
.table th {
|
||||
padding:5px 7px;
|
||||
font-weight:bold;
|
||||
text-align:left;
|
||||
font-size:0.9em;
|
||||
border-bottom:1px solid #ddd;
|
||||
}
|
||||
.table td {
|
||||
padding:9px 7px;
|
||||
border-left:1px solid #ddd;
|
||||
}
|
||||
.table tr td:first-child {border-left:0;}
|
||||
|
||||
.table tr {
|
||||
border-bottom:1px solid #fbfbfb;
|
||||
}
|
||||
.table tr:nth-child(even) {
|
||||
background:#F2F2F2;
|
||||
}
|
||||
|
||||
.table tr:last-child {
|
||||
border:0;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border:1px solid #ec252e;
|
||||
background:#ec252e;
|
||||
color:#fff;
|
||||
padding:8px 14px;
|
||||
background:-webkit-gradient(linear, 0 0, 0 bottom, from(#ec252e), to(#F05057));
|
||||
background:-webkit-linear-gradient(#ec252e, #F05057);
|
||||
background:-moz-linear-gradient(#ec252e, #F05057);
|
||||
background:-ms-linear-gradient(#ec252e, #F05057);
|
||||
background:-o-linear-gradient(#ec252e, #F05057);
|
||||
background:linear-gradient(#ec252e, #F05057);
|
||||
-webkit-border-radius:8px;
|
||||
-moz-border-radius:8px;
|
||||
border-radius:8px;
|
||||
}
|
||||
.success {
|
||||
border:1px solid #6e9e30;
|
||||
color:#fff;
|
||||
background:#0bbe2e;
|
||||
padding:8px 14px;
|
||||
background:-webkit-gradient(linear, 0 0, 0 bottom, from(#6e9e30), to(#87c03b));
|
||||
background:-webkit-linear-gradient(#6e9e30, #87c03b);
|
||||
background:-moz-linear-gradient(#6e9e30, #87c03b);
|
||||
background:-ms-linear-gradient(#6e9e30, #87c03b);
|
||||
background:-o-linear-gradient(#6e9e30, #87c03b);
|
||||
background:linear-gradient(#6e9e30, #87c03b);
|
||||
-webkit-border-radius:8px;
|
||||
-moz-border-radius:8px;
|
||||
border-radius:8px;
|
||||
}
|
||||
.message {
|
||||
border:1px solid #2180ff;
|
||||
color:#1f49bf;
|
||||
background:#bcd9ff;
|
||||
padding:8px 14px;
|
||||
-webkit-border-radius:8px;
|
||||
-moz-border-radius:8px;
|
||||
border-radius:8px;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width:480px) { /* Smartphone custom styles */
|
||||
}
|
||||
|
||||
@media only screen and (max-width:768px) { /* Tablet custom styles */
|
||||
}
|
162
www/res/style.css
Normal file
162
www/res/style.css
Normal file
|
@ -0,0 +1,162 @@
|
|||
/* "Simpliste" template. Renat Rafikov. http://cssr.ru/simpliste/ */
|
||||
|
||||
@import url('reset.css');
|
||||
@import url('skin.css');
|
||||
|
||||
|
||||
/* Columns
|
||||
-------
|
||||
.col_33 | .col_33 | .col_33
|
||||
.clearfix
|
||||
-------
|
||||
.col_75 | .col_25
|
||||
.clearfix
|
||||
-------
|
||||
.col_66 | .col_33
|
||||
.clearfix
|
||||
-------
|
||||
.col_50 | .col_50
|
||||
.clearfix
|
||||
-------
|
||||
.col_100
|
||||
-------
|
||||
*/
|
||||
.col_25 {
|
||||
width:23%;
|
||||
margin:0 2% 0 0;
|
||||
float:left;
|
||||
}
|
||||
.col_33 {
|
||||
width:31%;
|
||||
margin:0 2% 0 0;
|
||||
float:left;
|
||||
}
|
||||
.col_50 {
|
||||
width:48%;
|
||||
margin:0 2% 0 0;
|
||||
float:left;
|
||||
}
|
||||
.col_66 {
|
||||
width:64%;
|
||||
margin:0 2% 0 0;
|
||||
float:left;
|
||||
}
|
||||
.col_75 {
|
||||
width:73%;
|
||||
margin:0 2% 0 0;
|
||||
float:left;
|
||||
}
|
||||
.col_100 {
|
||||
width:98%;
|
||||
margin:0 2% 0 0;
|
||||
}
|
||||
|
||||
.col_25.wrap { width:25%; margin:0;}
|
||||
.col_33.wrap { width:33%; margin:0;}
|
||||
.col_50.wrap { width:50%; margin:0;}
|
||||
.col_66.wrap { width:66%; margin:0;}
|
||||
.col_75.wrap { width:75%; margin:0;}
|
||||
.col_100.wrap { width:100%; margin:0;}
|
||||
/* End columns */
|
||||
|
||||
|
||||
/* Helper classes */
|
||||
.center {text-align:center;}
|
||||
.left {text-align:left;}
|
||||
.right {text-align:right;}
|
||||
|
||||
.img_floatleft {float:left; margin:0 10px 5px 0;}
|
||||
.img_floatright {float:right; margin:0 0 5px 10px;}
|
||||
|
||||
.img {max-width:100%;}
|
||||
/* End helper classes */
|
||||
|
||||
a.button { color:auto; }
|
||||
|
||||
@media only screen and (max-width:480px) { /* Smartphone */
|
||||
.header {
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
.logo{
|
||||
display:block;
|
||||
float:none;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.menu_main {
|
||||
width:100%;
|
||||
text-align:center;
|
||||
float:none;
|
||||
padding:0;
|
||||
margin:1em 0 0 0;
|
||||
}
|
||||
|
||||
.menu_main a {
|
||||
display:inline-block;
|
||||
padding:7px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
width:100%;
|
||||
float:none;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-bottom:0;
|
||||
}
|
||||
|
||||
.menu_bottom {
|
||||
width:100%;
|
||||
float:none;
|
||||
text-align:center;
|
||||
margin:1em 0 0 0;
|
||||
padding:0;
|
||||
}
|
||||
.menu_bottom a {
|
||||
display:inline-block;
|
||||
padding:6px;
|
||||
}
|
||||
|
||||
.form textarea {
|
||||
width:100%;
|
||||
}
|
||||
.form label {
|
||||
padding:10px 0 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width:768px) { /* Tablet */
|
||||
.col_25,
|
||||
.col_33,
|
||||
.col_66,
|
||||
.col_50 ,
|
||||
.col_75 {
|
||||
width:98%;
|
||||
float:none;
|
||||
}
|
||||
|
||||
.form label {
|
||||
padding:10px 0 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media print { /* Printer */
|
||||
* { background:transparent !important; color:black !important; text-shadow:none !important; filter:none !important; -ms-filter:none !important; }
|
||||
a, a:visited { color:#444 !important; text-decoration:underline; }
|
||||
a[href]:after { content:" (" attr(href) ")"; }
|
||||
abbr[title]:after { content:" (" attr(title) ")"; }
|
||||
pre, blockquote { border:1px solid #999; page-break-inside:avoid; }
|
||||
thead { display:table-header-group; }
|
||||
tr, img { page-break-inside:avoid; }
|
||||
img { max-width:100% !important; }
|
||||
@page { margin:0.5cm; }
|
||||
p, h2, h3 { orphans:3; widows:3; }
|
||||
h2, h3{ page-break-after:avoid; }
|
||||
|
||||
.header, .footer, .form {display:none;}
|
||||
.col_33, .col_66, .col_50 { width:98%; float:none; }
|
||||
}
|
Reference in a new issue