Skip to content

Commit e2847af

Browse files
author
anrs
committed
feat: transfer tool for renaming to workload
1 parent 9490bac commit e2847af

File tree

2 files changed

+278
-16
lines changed

2 files changed

+278
-16
lines changed
+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
import argparse
4+
import functools
5+
import json
6+
import os
7+
import sys
8+
9+
import etcd3
10+
11+
def remove_prefix(s, prefix):
12+
return s[len(prefix):].lstrip('/') if s.startswith(prefix) else s
13+
14+
def range_prefix(meta, obj_prefix, fn):
15+
orig_prefix = os.path.join(meta.orig_root_prefix, obj_prefix)
16+
17+
for orig_value, orig_meta in meta.etcd.get_prefix(orig_prefix):
18+
orig_key = orig_meta.key.decode('utf-8')
19+
objname = remove_prefix(orig_key, orig_prefix)
20+
21+
new_key = fn(objname, orig_value.decode('utf-8'))
22+
if new_key:
23+
print('convert %s to %s' % (orig_key, new_key))
24+
25+
26+
class Pod(object):
27+
28+
def __init__(self, meta):
29+
''' initializes a pod transfer '''
30+
self.meta = meta
31+
self.pod_prefix = 'pod/info'
32+
self.range_prefix = functools.partial(range_prefix, self.meta)
33+
34+
def trans(self):
35+
self.range_prefix(self.pod_prefix, self._trans)
36+
37+
def _trans(self, podname, orig_value):
38+
new_key = os.path.join(self.meta.new_root_prefix, self.pod_prefix, podname)
39+
self.meta.etcd.put(new_key, orig_value)
40+
return new_key
41+
42+
43+
class Node(object):
44+
45+
def __init__(self, meta):
46+
self.meta = meta
47+
self.info_prefix = 'node'
48+
self.range_prefix = functools.partial(range_prefix, self.meta, self.info_prefix)
49+
self.nodes = {}
50+
51+
def trans(self):
52+
self.range_prefix(self._trans_info)
53+
self.range_prefix(self._trans_pod)
54+
self.range_prefix(self._trans_cert)
55+
self.range_prefix(self._trans_workload)
56+
57+
def _trans_info(self, nodename, orig_value):
58+
# skipping extra info.
59+
if ':' in nodename:
60+
return
61+
62+
self.nodes[nodename] = json.loads(orig_value)
63+
64+
new_key = os.path.join(self.meta.new_root_prefix, self.info_prefix, nodename)
65+
self.meta.etcd.put(new_key, orig_value)
66+
return new_key
67+
68+
def _trans_pod(self, node_pod_pair, orig_value):
69+
# parsering node-pod-pair itself only
70+
if ':pod/' not in node_pod_pair:
71+
return
72+
73+
podname, _, nodename = node_pod_pair.partition(':pod/')
74+
if not (podname and nodename):
75+
raise ValueError('invalid podname or nodename for %s' % node_pod_pair)
76+
77+
new_key = os.path.join(self.meta.new_root_prefix, self.info_prefix, '%s:pod' % podname, nodename)
78+
self.meta.etcd.put(new_key, orig_value)
79+
return new_key
80+
81+
def _trans_cert(self, cert_key, orig_value):
82+
nodename, _, cert_type = cert_key.partition(':')
83+
84+
# parsering orig_key which ends with :ca, :cert, :key only
85+
if cert_type not in ('ca', 'cert', 'key'):
86+
return
87+
88+
new_key = os.path.join(self.meta.new_root_prefix, self.info_prefix, '%s:%s' % (nodename, cert_type))
89+
self.meta.etcd.put(new_key, orig_value)
90+
return new_key
91+
92+
def _trans_workload(self, node_wrk_pair, orig_value):
93+
nodename, _, wrk_id = node_wrk_pair.partition(':containers/')
94+
95+
# parsering orig_key which belongs node-workload pair only.
96+
if not (nodename and wrk_id):
97+
return
98+
99+
new_key = os.path.join(self.meta.new_root_prefix, self.info_prefix, '%s:worklods' % nodename, wrk_id)
100+
wrk = Workload.conv(orig_value, self)
101+
self.meta.etcd.put(new_key, json.dumps(wrk))
102+
return new_key
103+
104+
def get_numa_node(self, cpumap, nodename):
105+
''' ref core types/core.go GetNUMANode func. '''
106+
numa_node_id = ""
107+
108+
node = self.nodes.get(nodename)
109+
if not node:
110+
raise ValueError('invalid nodename %s' % nodename)
111+
112+
numa = node.get('numa')
113+
if not numa:
114+
return numa_node_id
115+
116+
for cpu_id in cpumap:
117+
mem_node = numa.get(cpu_id)
118+
if not mem_node:
119+
continue
120+
121+
if numa_node_id == '':
122+
numa_node_id = mem_node
123+
elif numa_node_id != mem_node:
124+
numa_node_id = ''
125+
126+
return numa_node_id
127+
128+
129+
class Workload(object):
130+
131+
def __init__(self, meta, node_transfer):
132+
''' initializes a workload transfer '''
133+
self.meta = meta
134+
self.container_prefix = 'containers'
135+
self.deploy_prefix = 'deploy'
136+
self.range_prefix = functools.partial(range_prefix, self.meta)
137+
self.node_transfer = node_transfer
138+
139+
def trans(self):
140+
self.range_prefix(self.container_prefix, self._trans_container)
141+
self.range_prefix(self.deploy_prefix, self._trans_deploy)
142+
143+
def _trans_container(self, wrk_id, orig_value):
144+
new_key = os.path.join(self.meta.new_root_prefix, self.container_prefix, wrk_id)
145+
wrk = self.conv(orig_value, self.node_transfer)
146+
self.meta.etcd.put(new_key, json.dumps(wrk))
147+
return new_key
148+
149+
def _trans_deploy(self, deploy_key, orig_value):
150+
parts = deploy_key.split('/')
151+
if len(parts) != 4:
152+
raise ValueError('invalid deploy key: %s' % deploy_key)
153+
154+
appname, entrypoint, nodename, wrk_id = parts
155+
156+
new_key = os.path.join(self.meta.new_root_prefix, self.deploy_prefix, appname, entrypoint, nodename, wrk_id)
157+
wrk = self.conv(orig_value, self.node_transfer)
158+
self.meta.etcd.put(new_key, json.dumps(wrk))
159+
160+
return new_key
161+
162+
@classmethod
163+
def conv(cls, orig_value, node_transfer):
164+
165+
def delete(*keys):
166+
for k in keys:
167+
try:
168+
del dic[k]
169+
except KeyError:
170+
pass
171+
172+
del_keys = set()
173+
def get(new_field, orig_field, transit_field, default=None):
174+
value = None
175+
176+
# don't use dic.get(new_field, dic[orig_field]),
177+
# due to there isn't orig_field but has new_field.
178+
if new_field in dic:
179+
value = dic[new_field]
180+
elif transit_field in dic:
181+
value = dic[transit_field]
182+
else:
183+
if default is None:
184+
value = dic[orig_field]
185+
else:
186+
value = dic.get(orig_field, default)
187+
188+
del_keys.update({orig_field, transit_field})
189+
190+
return value
191+
192+
dic = json.loads(orig_value)
193+
dic['cpu'] = get('CPU', 'cpu', 'CPU')
194+
195+
dic.update(dict(
196+
create_time=1553990400,
197+
cpu_quota_request=get('cpu_quota_request', 'quota', 'CPUQuotaRequest'),
198+
cpu_quota_limit=get('cpu_quota_limit', 'quota', 'CPUQuotaLimit'),
199+
memory_request=get('memory_request', 'memory', 'MemoryRequest'),
200+
memory_limit=get('memory_limit', 'memory', 'MemoryLimit'),
201+
volume_request=get('volume_request', 'volumes', 'VolumeRequest'),
202+
volume_limit=get('volume_limit', 'volumes', 'VolumeLimit'),
203+
volume_plan_request=get('volume_plan_request', 'volume_plan', 'VolumePlanRequest', default={}),
204+
volume_plan_limit=get('volume_plan_limit', 'volume_plan', 'VolumePlanLimit', default={}),
205+
volume_changed=dic.get('volume_changed', False),
206+
storage_request=get('storage_request', 'storage', 'StorageRequest'),
207+
storage_limit=get('storage_limit', 'storage', 'StorageLimit'),
208+
))
209+
210+
dic['numa_node'] = ''
211+
if dic['cpu'] and node_transfer:
212+
numa_node = node_transfer.get_numa_node(dic['cpu'], dic['nodename'])
213+
dic['numa_node'] = dic.get('NUMANode', numa_node)
214+
215+
# don't removing *cpu* from the original dict.
216+
try:
217+
del_keys.remove('cpu')
218+
except KeyError:
219+
pass
220+
221+
del_keys.update({'softlimit', 'VolumeChanged', 'NUMANode'})
222+
delete(*list(del_keys))
223+
224+
return dic
225+
226+
227+
class Transfer(object):
228+
229+
def __init__(self, etcd, orig_root_prefix, new_root_prefix):
230+
''' initializes a transfer which includes common utilities '''
231+
self.etcd = etcd
232+
self.orig_root_prefix = orig_root_prefix
233+
self.new_root_prefix = new_root_prefix
234+
235+
def trans(self):
236+
Pod(self).trans()
237+
238+
node_transfer = Node(self)
239+
node_transfer.trans()
240+
241+
Workload(self, node_transfer).trans()
242+
243+
def getargs():
244+
ap = argparse.ArgumentParser()
245+
ap.add_argument('-o', dest='orig_root_prefix', help='original prefix', default='/eru')
246+
ap.add_argument('-n', dest='new_root_prefix', help='new prefix', default='/eru2')
247+
ap.add_argument('--etcd-host', default='127.0.0.1')
248+
ap.add_argument('--etcd-port', type=int, default=2379)
249+
return ap.parse_args()
250+
251+
def connect_etcd(host, port):
252+
return etcd3.client(host=host, port=port)
253+
254+
def main():
255+
args = getargs()
256+
etcd = connect_etcd(args.etcd_host, args.etcd_port)
257+
trans = Transfer(etcd, args.orig_root_prefix, args.new_root_prefix)
258+
trans.trans()
259+
return 0
260+
261+
if __name__ == '__main__':
262+
sys.exit(main())

types/resource.go

+16-16
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ type ResourceOptions struct {
2323

2424
// ResourceMeta for messages and workload to store
2525
type ResourceMeta struct {
26-
CPUQuotaRequest float64
27-
CPUQuotaLimit float64
28-
CPU ResourceMap
29-
NUMANode string
30-
31-
MemoryRequest int64
32-
MemoryLimit int64
33-
34-
VolumeRequest VolumeBindings
35-
VolumeLimit VolumeBindings
36-
VolumePlanRequest VolumePlan
37-
VolumePlanLimit VolumePlan
38-
VolumeChanged bool
39-
40-
StorageRequest int64
41-
StorageLimit int64
26+
CPUQuotaRequest float64 `json:"cpu_quota_request"`
27+
CPUQuotaLimit float64 `json:"cpu_quota_limit"`
28+
CPU ResourceMap `json:"cpu"`
29+
NUMANode string `json:"numa_node"`
30+
31+
MemoryRequest int64 `json:"memory_request"`
32+
MemoryLimit int64 `json:"memory_limit"`
33+
34+
VolumeRequest VolumeBindings `json:"volume_request"`
35+
VolumeLimit VolumeBindings `json:"volume_limit"`
36+
VolumePlanRequest VolumePlan `json:"volume_plan_request"`
37+
VolumePlanLimit VolumePlan `json:"volume_plan_limit"`
38+
VolumeChanged bool `json:"volume_changed"`
39+
40+
StorageRequest int64 `json:"storage_request"`
41+
StorageLimit int64 `json:"storage_limit"`
4242
}
4343

4444
// ResourceType .

0 commit comments

Comments
 (0)