3 from nevow import rend, inevow
4 from nevow.static import File as nevow_File
5 from nevow.util import resource_filename
6 from foolscap.api import SturdyRef
7 from twisted.internet import address
10 from allmydata import get_package_versions_string
11 from allmydata.util import idlib
12 from allmydata.web.common import getxmlfile, get_arg
14 class IntroducerRoot(rend.Page):
17 docFactory = getxmlfile("introducer.xhtml")
19 child_operations = None
21 def __init__(self, introducer_node):
22 self.introducer_node = introducer_node
23 self.introducer_service = introducer_node.getServiceNamed("introducer")
24 rend.Page.__init__(self, introducer_node)
25 static_dir = resource_filename("allmydata.web", "static")
26 for filen in os.listdir(static_dir):
27 self.putChild(filen, nevow_File(os.path.join(static_dir, filen)))
29 def renderHTTP(self, ctx):
30 t = get_arg(inevow.IRequest(ctx), "t")
32 return self.render_JSON(ctx)
33 return rend.Page.renderHTTP(self, ctx)
35 def render_JSON(self, ctx):
39 subscribers = self.introducer_service.get_subscribers()
40 for (service_name, ign, ign, ign) in subscribers:
41 if service_name not in counts:
42 counts[service_name] = 0
43 counts[service_name] += 1
44 res["subscription_summary"] = counts
46 announcement_summary = {}
48 for a in self.introducer_service.get_announcements().values():
50 service_name = ann["service-name"]
51 if service_name not in announcement_summary:
52 announcement_summary[service_name] = 0
53 announcement_summary[service_name] += 1
54 if service_name not in service_hosts:
55 service_hosts[service_name] = set()
56 # it's nice to know how many distinct hosts are available for
57 # each service. We define a "host" by a set of addresses
58 # (hostnames or ipv4 addresses), which we extract from the
59 # connection hints. In practice, this is usually close
60 # enough: when multiple services are run on a single host,
61 # they're usually either configured with the same addresses,
62 # or setLocationAutomatically picks up the same interfaces.
63 furl = ann["anonymous-storage-FURL"]
64 locations = SturdyRef(furl).getTubRef().getLocations()
65 # list of tuples, ("ipv4", host, port)
66 host = frozenset([hint[1]
68 if hint[0] == "ipv4"])
69 service_hosts[service_name].add(host)
70 res["announcement_summary"] = announcement_summary
71 distinct_hosts = dict([(name, len(hosts))
73 in service_hosts.iteritems()])
74 res["announcement_distinct_hosts"] = distinct_hosts
76 return simplejson.dumps(res, indent=1) + "\n"
78 # FIXME: This code is duplicated in root.py and introweb.py.
79 def data_version(self, ctx, data):
80 return get_package_versions_string()
81 def data_import_path(self, ctx, data):
82 return str(allmydata).replace("/", "/ ") # XXX kludge for wrapping
83 def data_my_nodeid(self, ctx, data):
84 return idlib.nodeid_b2a(self.introducer_node.nodeid)
86 def render_announcement_summary(self, ctx, data):
88 for a in self.introducer_service.get_announcements().values():
90 service_name = ann["service-name"]
91 if service_name not in services:
92 services[service_name] = 0
93 services[service_name] += 1
94 service_names = services.keys()
96 return ", ".join(["%s: %d" % (service_name, services[service_name])
97 for service_name in service_names])
99 def render_client_summary(self, ctx, data):
101 clients = self.introducer_service.get_subscribers()
102 for (service_name, ign, ign, ign) in clients:
103 if service_name not in counts:
104 counts[service_name] = 0
105 counts[service_name] += 1
106 return ", ".join([ "%s: %d" % (name, counts[name])
107 for name in sorted(counts.keys()) ] )
109 def data_services(self, ctx, data):
110 introsvc = self.introducer_service
112 for a in introsvc.get_announcements().values():
113 (_, _, ann, when) = a
114 if ann["service-name"] == "stub_client":
116 services.append( (when, ann) )
117 services.sort(key=lambda x: (x[1]["service-name"], x[1]["nickname"]))
119 #services.sort(lambda a,b: cmp( (a[1][1], a), (b[1][1], b) ) )
120 # service_name was the primary key, then the whole tuple (starting
121 # with the furl) was the secondary key
124 def render_service_row(self, ctx, (since,ann)):
125 sr = SturdyRef(ann["anonymous-storage-FURL"])
127 advertised = self.show_location_hints(sr)
128 ctx.fillSlots("peerid", nodeid)
129 ctx.fillSlots("nickname", ann["nickname"])
130 ctx.fillSlots("advertised", " ".join(advertised))
131 ctx.fillSlots("connected", "?")
132 TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
133 ctx.fillSlots("announced",
134 time.strftime(TIME_FORMAT, time.localtime(since)))
135 ctx.fillSlots("version", ann["my-version"])
136 ctx.fillSlots("service_name", ann["service-name"])
139 def data_subscribers(self, ctx, data):
140 return self.introducer_service.get_subscribers()
142 def render_subscriber_row(self, ctx, s):
143 (service_name, since, info, rref) = s
144 nickname = info.get("nickname", "?")
145 version = info.get("my-version", "?")
147 sr = rref.getSturdyRef()
148 # if the subscriber didn't do Tub.setLocation, nodeid will be None
149 nodeid = sr.tubID or "?"
150 ctx.fillSlots("peerid", nodeid)
151 ctx.fillSlots("nickname", nickname)
152 advertised = self.show_location_hints(sr)
153 ctx.fillSlots("advertised", " ".join(advertised))
154 remote_host = rref.tracker.broker.transport.getPeer()
155 if isinstance(remote_host, address.IPv4Address):
156 remote_host_s = "%s:%d" % (remote_host.host, remote_host.port)
158 # loopback is a non-IPv4Address
159 remote_host_s = str(remote_host)
160 ctx.fillSlots("connected", remote_host_s)
161 TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
162 ctx.fillSlots("since",
163 time.strftime(TIME_FORMAT, time.localtime(since)))
164 ctx.fillSlots("version", version)
165 ctx.fillSlots("service_name", service_name)
168 def show_location_hints(self, sr, ignore_localhost=True):
170 for hint in sr.locationHints:
171 if isinstance(hint, str):
172 # Foolscap-0.2.5 and earlier used strings in .locationHints
173 if ignore_localhost and hint.startswith("127.0.0.1"):
175 advertised.append(hint.split(":")[0])
177 # Foolscap-0.2.6 and later use tuples of ("ipv4", host, port)
178 if hint[0] == "ipv4":
180 if ignore_localhost and host == "127.0.0.1":
182 advertised.append(hint[1])