Skip to content

Commit 9cc9dcc

Browse files
author
Ron Gorodetzky
committed
initial (re)import
0 parents  commit 9cc9dcc

File tree

152 files changed

+13128
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

152 files changed

+13128
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.pyc
2+
*~
3+
build
4+
dist
5+
src/clusto.egg-info
6+
doc/.build

README

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
*****
2+
THIS IS OUT OF DATE AND NEEDS TO BE UPDATED
3+
*****
4+
5+
=== A lot of this stuff has changed I'll have to update it ===
6+
7+
Things seem to fall into 3 general categories:
8+
9+
1. Equipment - Things like servers, switches, racks, routers, etc.
10+
11+
2. Parts - Things like NICS, ports, U (rack space), etc.
12+
13+
3. Resources - IP ranges/network blocks, space (U, datacenter space), etc.
14+
15+
These are loose categories
16+
17+
18+
Kinds of matching:
19+
20+
1. the given key value pairs exist in Thing
21+
2. for the given keys the values match exactly in the Thing
22+
3. the given key/values exactly match attributes of Thing
23+
24+
25+
Questions I'd like to be able to answer questions like:
26+
27+
* get 4 servers in datacenter Z with status == unallocated
28+
each in a different rack
29+
30+
servers = []
31+
d = clusto.getByName('datacenterZ')
32+
racks = d.getConnectedMatching(AttributeDict(Racks.allMetaAttrs()))
33+
for r in racks:
34+
s = r.getConnectedMatching(Servers.allMetaAttrs() + [('status', 'unallocated')])
35+
servers.append(s.pop())
36+
37+
38+
* get console for server X
39+
* add service W on server X to loadbalancer vip Y
40+
41+
clusto expressions (maybe)
42+
-----------------------
43+
44+
driver:Console connected to name:server2
45+
- return the console connected to 'server2'
46+
47+
driver:Server
48+
- return all serves
49+
50+
driver:Server connected to name:rack002
51+
- return servers in rack002
52+
53+
attr:foo,12 and driver:Server
54+
- return servers with attr foo,12
55+
56+
pattern
57+
-------
58+
alphanumeric
59+
* glob supported ?
60+
* regex supported ?
61+
62+
pattern types
63+
----------
64+
name:<somename>
65+
driver:<driver>
66+
attr:<key,val>
67+
68+
operators
69+
---------
70+
not, or, and,
71+
72+
functions
73+
---------
74+
haskey
75+
connected to
76+
77+
78+
clusto categorization ideas
79+
===========================
80+
81+
Class, Role, Service
82+
83+
Class is similar to the class concept from fai. The classes of a server, for
84+
example, might include information about what software is installed on that
85+
machine or how much ram the machine has or how many disks.
86+
87+
Role is a description of the job of a Thing. So a server could have the roles
88+
of Production, LiveWebserver, and Mail. These roles are generally more
89+
abstract and don't refer to specific software or hardware properties
90+
themselves. The can however require that the server be a member of certain
91+
Classes. So the Blog role might require that the server be connected to the
92+
Webserver class while the LiveSite role would require the server be connected
93+
to the Apache and BigMem classes.
94+
95+
A Service is an instance of a role. So you can have dbslave14... (not sure of
96+
the value of this)
97+
98+
A Pool is just a grouping of Things. For example you could have many Servers
99+
with a 'dbslave' role but with some in a 'production' pool and others in a
100+
'development' pool.
101+
102+
(how do you put two db roles on one machine?)
103+
( up to sysadmins. might use VMs, might use vservers (like openvz), might
104+
put things on separate ports)
105+
106+
107+
clusto command line ideas
108+
=========================
109+
110+
clusto connect foo1 foo2
111+
- connect foo1 and foo2
112+
113+
clusto power off server1
114+
115+
clusto locate server1
116+
- should return:
117+
server1 -> connected to port 4 on switch2a
118+
server1 -> connected to RU13 in rack101
119+
120+
clusto console server1
121+
- connect to console on server1
122+
123+
clusto attr del <key> [<value>]
124+
clusto attr add <key> <value> [<value>] [<value>] ...
125+
clusto attr get <key>
126+
clusto attr set <key> <value> [<value>] [<value>] ...
127+
- modify Thing attributes
128+
129+
clusto network setvlan server1 eth1 vlan203
130+
- put eth1 on server1 onto vlan203
131+
132+
clusto rebuild server1
133+
- reinstall operating system onto server1
134+
135+
Reserverd Attrs
136+
===============
137+
138+
_ip
139+
_in

contrib/README

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
dhcpgen.py -- Generate a list of host sections for ISC dhcpd
2+
discovery.py -- Module for discovering information about hosts, not directly
3+
callable
4+
etchosts.py -- Attempt to discover and create clusto server objects based on
5+
the content of a hosts file. May need modification in main() to
6+
achieve the desired effect.
7+
parsearp.py -- Imported by etchosts.py, allows discovery and creation of objects
8+
based on the output of scapy's arpping command
9+
sysinfo.py -- Uses paramiko to connect to servers and gather information about
10+
disks, processors, and memory to store in clusto attributes
11+
watcher.py -- If run with the "snmp" option, it watches for SNMP traps and
12+
creates new clusto objects bound to the correct rack and switch
13+
port. If run with the "dhcp" option, it will watch for DHCP
14+
requests containing vendor IDs matching PXE clients and IPMI
15+
cards. Associates the MAC address with the proper interface on
16+
the server object.

contrib/clustohttp.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
try:
2+
import simplejson as json
3+
except ImportError:
4+
import json
5+
6+
from urllib import urlencode, quote
7+
from urlparse import urlsplit, urljoin
8+
import httplib
9+
import logging
10+
11+
from pprint import pprint
12+
13+
def request(method, url, body='', headers={}):
14+
logging.debug('%s %s' % (method, url))
15+
if type(body) != type(''):
16+
body = urlencode(body)
17+
url = urlsplit(url, 'http')
18+
19+
conn = httplib.HTTPConnection(url.hostname, url.port)
20+
if url.query:
21+
query = '%s?%s' % (url.path, url.query)
22+
else:
23+
query = url.path
24+
conn.request(method, query, body, headers)
25+
response = conn.getresponse()
26+
length = response.getheader('Content-length', None)
27+
if length:
28+
data = response.read(int(length))
29+
else:
30+
data = response.read()
31+
conn.close()
32+
if response.status >= 400:
33+
logging.debug('Server error %s: %s' % (response.status, data))
34+
return (response.status, response.getheaders(), data)
35+
36+
class ClustoProxy(object):
37+
def __init__(self, url):
38+
self.url = url
39+
40+
def get_entities(self, **kwargs):
41+
for k, v in kwargs.items():
42+
if k == 'attrs':
43+
kwargs[k] = json.dumps(v)
44+
status, headers, response = request('POST', self.url + '/query/get_entities?%s' % urlencode(kwargs))
45+
if status != 200:
46+
raise Exception(response)
47+
return [EntityProxy(self.url + x) for x in json.loads(response)]
48+
49+
def get_by_name(self, name):
50+
status, headers, response = request('GET', self.url + '/query/get_by_name?name=%s' % quote(name))
51+
if status != 200:
52+
raise Exception(response)
53+
obj = json.loads(response)
54+
return EntityProxy(self.url + obj['object'])
55+
56+
def get_from_pools(self, pools, clusto_types=None):
57+
url = self.url + '/query/get_from_pools?pools=%s' % ','.join(pools)
58+
if clusto_types:
59+
url += '&types=' + ','.join(clusto_types)
60+
status, headers, response = request('GET', url)
61+
if status != 200:
62+
raise Exception(response)
63+
return [EntityProxy(self.url + x) for x in json.loads(response)]
64+
65+
def get_ip_manager(self, ip):
66+
status, headers, response = request('GET', self.url + '/query/get_ip_manager?ip=%s' % ip)
67+
if status != 200:
68+
raise Exception(response)
69+
return EntityProxy(self.url + json.loads(response))
70+
71+
class EntityProxy(object):
72+
def __init__(self, url):
73+
self.url = url
74+
self.name = self.url.rsplit('/', 1)[1]
75+
76+
def __getattr__(self, action):
77+
def method(**kwargs):
78+
data = {}
79+
for k, v in kwargs.items():
80+
if isinstance(v, bool):
81+
v = int(v)
82+
if not type(v) in (int, str, unicode):
83+
v = json.dumps(v)
84+
data[k] = v
85+
if data:
86+
status, headers, response = request('GET', '%s/%s?%s' % (self.url, action, urlencode(data)))
87+
else:
88+
status, headers, response = request('GET', '%s/%s' % (self.url, action))
89+
if status != 200:
90+
raise Exception(response)
91+
if response:
92+
return json.loads(response)
93+
else:
94+
return None
95+
return method
96+
97+
def contents(self):
98+
return [EntityProxy(urljoin(self.url, x)) for x in self.show()['contents']]
99+
100+
def parents(self):
101+
return [EntityProxy(urljoin(self.url, x)) for x in self.show()['parents']]
102+
103+
def attrs(self, **kwargs):
104+
return self.__getattr__('attrs')(**kwargs)['attrs']
105+
106+
def set_port_attr(self, porttype, portnum, key, value):
107+
return self.__getattr__('set_port_attr')(porttype=porttype, portnum=portnum, key=key, value=value)
108+
109+
def get_port_attr(self, porttype, portnum, key):
110+
return self.__getattr__('get_port_attr')(porttype=porttype, portnum=portnum, key=key)
111+
112+
def __str__(self):
113+
return urlsplit(self.url).path
114+
115+
def __repr__(self):
116+
return 'EntityProxy(%s)' % repr(self.url)
117+
118+
def test():
119+
clusto = ClustoProxy('http://127.0.0.1:9996')
120+
server = clusto.get_entities(attrs=[{'subkey': 'mac', 'value': '00:a0:d1:e9:3d:dc'}])
121+
server = server[0]
122+
print server
123+
assert server.name == 's0104'
124+
attr = server.get_port_attr('nic-eth', 1, 'mac')
125+
server.set_port_attr('nic-eth', 1, 'mac', attr)
126+
newattr = server.get_port_attr('nic-eth', 1, 'mac')
127+
print repr((attr, newattr))
128+
assert newattr == attr
129+
#print server.parents()
130+
#obj = clusto.get_by_name('s1100')
131+
#pprint(obj.ports())
132+
#pprint(obj.attrs(key='dhcp', merge_container_attrs=True))
133+
#webservers = clusto.get_from_pools(['webservers-lolcat', 'production'])
134+
#pprint(webservers)
135+
#pprint(webservers[0].contents())
136+
137+
if __name__ == '__main__':
138+
test()

contrib/dhcpgen.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from clusto.scripthelpers import init_script
2+
from clusto.drivers import PenguinServer, IPManager
3+
import clusto
4+
import sys
5+
6+
from traceback import format_exc
7+
8+
ATTR_MAP = {
9+
'tftp-server': 'next-server',
10+
'tftp-filename': 'filename',
11+
'nfsroot': 'option root-path',
12+
}
13+
14+
def main():
15+
#for server in clusto.get_entities(clusto_drivers=[PenguinServer]):
16+
for server in clusto.get_by_name('fai').contents():
17+
out = 'host %s { ' % server.name
18+
19+
try:
20+
mac = server.get_port_attr('nic-eth', 1, 'mac')
21+
if not mac:
22+
sys.stderr.write('No nic-eth:1 mac for %s\n' % server.name)
23+
continue
24+
out += 'hardware ethernet %s; ' % mac
25+
except:
26+
sys.stderr.write(format_exc() + '\n')
27+
continue
28+
29+
ip = IPManager.get_ips(server)
30+
if ip:
31+
ip = ip[0]
32+
out += 'fixed-address %s; ' % ip
33+
34+
options = {}
35+
for attr in server.attrs(key='dhcp', merge_container_attrs=True):
36+
if attr.subkey in options: continue
37+
if not attr.subkey in ATTR_MAP:
38+
sys.stderr.write('Unknown subkey: %s\n' % attr.subkey)
39+
continue
40+
options[ATTR_MAP[attr.subkey]] = attr.value
41+
42+
for key, value in options.items():
43+
out += '%s %s; ' % (key, value)
44+
45+
out += '}\n'
46+
47+
sys.stdout.write(out)
48+
sys.stdout.flush()
49+
50+
if __name__ == '__main__':
51+
init_script()
52+
main()

0 commit comments

Comments
 (0)