1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | import time |
---|
5 | from twisted.web.template import Element, XMLFile, renderElement, renderer |
---|
6 | from twisted.python.filepath import FilePath |
---|
7 | import allmydata |
---|
8 | from allmydata.util import idlib, jsonbytes as json |
---|
9 | from allmydata.web.common import ( |
---|
10 | render_time, |
---|
11 | MultiFormatResource, |
---|
12 | SlotsSequenceElement, |
---|
13 | add_static_children, |
---|
14 | ) |
---|
15 | |
---|
16 | |
---|
17 | class IntroducerRoot(MultiFormatResource): |
---|
18 | """ |
---|
19 | A ``Resource`` intended as the root resource for introducers. |
---|
20 | |
---|
21 | :param _IntroducerNode introducer_node: The introducer node to template |
---|
22 | information about. |
---|
23 | """ |
---|
24 | |
---|
25 | def __init__(self, introducer_node): |
---|
26 | super(IntroducerRoot, self).__init__() |
---|
27 | self.introducer_node = introducer_node |
---|
28 | self.introducer_service = introducer_node.getServiceNamed("introducer") |
---|
29 | # necessary as a root Resource |
---|
30 | self.putChild(b"", self) |
---|
31 | add_static_children(self) |
---|
32 | |
---|
33 | def _create_element(self): |
---|
34 | """ |
---|
35 | Create a ``IntroducerRootElement`` which can be flattened into an HTML |
---|
36 | response. |
---|
37 | """ |
---|
38 | return IntroducerRootElement( |
---|
39 | self.introducer_node, self.introducer_service) |
---|
40 | |
---|
41 | def render_HTML(self, req): |
---|
42 | """ |
---|
43 | Render an HTML template describing this introducer node. |
---|
44 | """ |
---|
45 | return renderElement(req, self._create_element()) |
---|
46 | |
---|
47 | def render_JSON(self, req): |
---|
48 | """ |
---|
49 | Render JSON describing this introducer node. |
---|
50 | """ |
---|
51 | res = {} |
---|
52 | |
---|
53 | counts = {} |
---|
54 | for s in self.introducer_service.get_subscribers(): |
---|
55 | if s.service_name not in counts: |
---|
56 | counts[s.service_name] = 0 |
---|
57 | counts[s.service_name] += 1 |
---|
58 | res[u"subscription_summary"] = counts |
---|
59 | |
---|
60 | announcement_summary = {} |
---|
61 | for ad in self.introducer_service.get_announcements(): |
---|
62 | service_name = ad.service_name |
---|
63 | if service_name not in announcement_summary: |
---|
64 | announcement_summary[service_name] = 0 |
---|
65 | announcement_summary[service_name] += 1 |
---|
66 | res[u"announcement_summary"] = announcement_summary |
---|
67 | |
---|
68 | return (json.dumps(res, indent=1) + "\n").encode("utf-8") |
---|
69 | |
---|
70 | |
---|
71 | class IntroducerRootElement(Element): |
---|
72 | """ |
---|
73 | An ``Element`` HTML template which can be flattened to describe this |
---|
74 | introducer node. |
---|
75 | |
---|
76 | :param _IntroducerNode introducer_node: The introducer node to describe. |
---|
77 | :param IntroducerService introducer_service: The introducer service created |
---|
78 | by the node. |
---|
79 | """ |
---|
80 | |
---|
81 | loader = XMLFile(FilePath(__file__).sibling("introducer.xhtml")) |
---|
82 | |
---|
83 | def __init__(self, introducer_node, introducer_service): |
---|
84 | super(IntroducerRootElement, self).__init__() |
---|
85 | self.introducer_node = introducer_node |
---|
86 | self.introducer_service = introducer_service |
---|
87 | self.node_data_dict = { |
---|
88 | "my_nodeid": idlib.nodeid_b2a(self.introducer_node.nodeid), |
---|
89 | "version": allmydata.__full_version__, |
---|
90 | "import_path": str(allmydata).replace("/", "/ "), # XXX kludge for wrapping |
---|
91 | "rendered_at": render_time(time.time()), |
---|
92 | } |
---|
93 | |
---|
94 | @renderer |
---|
95 | def node_data(self, req, tag): |
---|
96 | return tag.fillSlots(**self.node_data_dict) |
---|
97 | |
---|
98 | @renderer |
---|
99 | def announcement_summary(self, req, tag): |
---|
100 | services = {} |
---|
101 | for ad in self.introducer_service.get_announcements(): |
---|
102 | if ad.service_name not in services: |
---|
103 | services[ad.service_name] = 0 |
---|
104 | services[ad.service_name] += 1 |
---|
105 | service_names = list(services.keys()) |
---|
106 | service_names.sort() |
---|
107 | return u", ".join(u"{}: {}".format(service_name, services[service_name]) |
---|
108 | for service_name in service_names) |
---|
109 | |
---|
110 | @renderer |
---|
111 | def client_summary(self, req, tag): |
---|
112 | counts = {} |
---|
113 | for s in self.introducer_service.get_subscribers(): |
---|
114 | if s.service_name not in counts: |
---|
115 | counts[s.service_name] = 0 |
---|
116 | counts[s.service_name] += 1 |
---|
117 | return u", ".join(u"{}: {}".format(name, counts[name]) |
---|
118 | for name in sorted(counts.keys())) |
---|
119 | |
---|
120 | @renderer |
---|
121 | def services(self, req, tag): |
---|
122 | services = self.introducer_service.get_announcements() |
---|
123 | services.sort(key=lambda ad: (ad.service_name, ad.nickname)) |
---|
124 | services = [{ |
---|
125 | "serverid": ad.serverid, |
---|
126 | "nickname": ad.nickname, |
---|
127 | "connection-hints": |
---|
128 | u"connection hints: " + u" ".join(ad.connection_hints), |
---|
129 | "connected": u"?", |
---|
130 | "announced": render_time(ad.when), |
---|
131 | "version": ad.version, |
---|
132 | "service_name": ad.service_name, |
---|
133 | } for ad in services] |
---|
134 | return SlotsSequenceElement(tag, services) |
---|
135 | |
---|
136 | @renderer |
---|
137 | def subscribers(self, req, tag): |
---|
138 | subscribers = [{ |
---|
139 | "nickname": s.nickname, |
---|
140 | "tubid": s.tubid, |
---|
141 | "connected": s.remote_address, |
---|
142 | "since": render_time(s.when), |
---|
143 | "version": s.version, |
---|
144 | "service_name": s.service_name, |
---|
145 | } for s in self.introducer_service.get_subscribers()] |
---|
146 | return SlotsSequenceElement(tag, subscribers) |
---|