14874: Adds admin script examples on documentation.
[arvados.git] / doc / admin / collection-managed-properties.html.textile.liquid
1 ---
2 layout: default
3 navsection: admin
4 title: Configuring collection's managed properties
5 ...
6
7 {% comment %}
8 Copyright (C) The Arvados Authors. All rights reserved.
9
10 SPDX-License-Identifier: CC-BY-SA-3.0
11 {% endcomment %}
12
13 Collection's managed properties allow a cluster administrator to enable some special behaviors regarding properties at creation & update times.
14 This page describes how to enable and configure these behaviors on the API server.
15
16 h3. API Server configuration
17
18 The @Collections.ManagedProperties@ setting from the @config.yml@ file is used for enabling any of the following behaviors:
19
20 h4. Pre-assigned property key & value
21
22 For every newly created collection, assign a predefined key/value pair if it isn't already passed at creation time:
23
24 <pre>
25 Collections:
26   ManagedProperties:
27     foo: {value: bar}
28 </pre>
29
30 h4. Original owner UUID
31
32 This behavior will assign to a property key the UUID of the user who owns the collection's contaning project.
33
34 <pre>
35 Collections:
36   ManagedProperties:
37     responsible_person_uuid: {function: original_owner}
38 </pre>
39
40 h4. Protected properties
41
42 If there's a need to prevent a non-admin user from modifying a specific property, even by its owner, the @protected@ attribute can be set to @true@, like so:
43
44 <pre>
45 Collections:
46   ManagedProperties:
47     responsible_person_uuid: {function: original_owner, protected: true}
48 </pre>
49
50 This property can be applied to any of the defined managed properties. If missing, it's assumed as being @false@ by default.
51
52 h3. Supporting example scripts
53
54 When enabling this feature, there may be pre-existing collections that won't have the managed properties just configured. The following script examples may be helpful to sync these older collections.
55
56 For the following examples we assume that the @responsible_person_uuid@ property is set as @{function: original_owner, protected: true}@.
57
58 h4. List uuid/names of collections without @responsible_person_uuid@ property
59
60 {% codeblock as python %}
61 import arvados
62
63 page = 100
64 offset = 0
65 cols = []
66
67 filters = [['properties.responsible_person_uuid', 'exists', False]]
68 api = arvados.api()
69 req = api.collections().list(filters=filters, select=['uuid', 'name'], limit=page).execute()
70
71 while True:
72     cols += [c for c in req['items']]
73     if req['items_available'] < offset+page:
74         break
75     offset += page
76     req = api.collections().list(filters=filters, select=['uuid', 'name'], limit=page, offset=offset).execute()
77
78 print("Found {} collections:".format(len(cols)))
79 for c in cols:
80     print('{}, "{}"'.format(c['uuid'], c['name']))
81 {% endcodeblock %}
82
83 h4. Update the @responsible_person_uuid@ property from nil to X in the project hierarchy rooted at P
84
85 {% codeblock as python %}
86 import arvados
87
88 def get_subproject_uuids(api, root_uuid):
89     page = 100
90     offset = 0
91     uuids = []
92     r = api.groups().list(
93         filters=[['owner_uuid', '=', '{}'.format(root_uuid)]],
94         select=['uuid'],
95         limit=page
96     ).execute()
97     while True:
98         for g in r['items']:
99             uuids += ([g['uuid']] + get_subproject_uuids(api, g['uuid']))
100         if r['items_available'] < offset+page:
101             break
102         offset += page
103         r = api.groups().list(
104             filters=[['owner_uuid', '=', '{}'.format(root_uuid)]],
105             select=['uuid'],
106             limit=page
107         ).execute()
108     return uuids
109
110 def get_cols(api, filters):
111     page = 100
112     offset = 0
113     uuids = []
114     r = api.collections().list(filters=filters, select=['uuid', 'properties'], limit=page).execute()
115     while True:
116         uuids += [c for c in r['items']]
117         if r['items_available'] < offset+page:
118             break
119         offset += page
120         r = api.collections().list(filters=filters, select=['uuid', 'properties'], limit=page).execute()
121     return uuids
122
123 root_uuid = 'zzzzz-j7d0g-ppppppppppppppp'
124 responsible_uuid = 'zzzzz-tpzed-xxxxxxxxxxxxxxx'
125
126 api = arvados.api()
127 for p_uuid in [root_uuid] + get_subproject_uuids(api, root_uuid):
128     f = [['properties.responsible_person_uuid', 'exists', False],
129          ['owner_uuid', '=', p_uuid]]
130     cols = get_cols(api, f)
131     print("Found {} collections owned by {}".format(len(cols), p_uuid))
132     for c in cols:
133         print(" - Updating collection {}".format(c["uuid"]))
134         props = c['properties']
135         props['responsible_person_uuid'] = responsible_uuid
136         api.collections().update(uuid=c['uuid'], body={'properties': props}).execute()
137 {% endcodeblock %}
138
139 h4. Update the @responsible_person_uuid@ property from X to Y
140
141 The following code should run with admin privileges, assuming that the managed property is @protected@.
142
143 {% codeblock as python %}
144 import arvados
145
146 old_uuid = "zzzzz-tpzed-xxxxxxxxxxxxxxx"
147 new_uuid = "zzzzz-tpzed-yyyyyyyyyyyyyyy"
148
149 page = 100
150 offset = 0
151 cols = []
152
153 filters = [['properties.responsible_person_uuid', '=', '{}'.format(old_uuid)]]
154 api = arvados.api()
155 req = api.collections().list(filters=filters, select=['uuid', 'properties'], limit=page).execute()
156
157 while True:
158     cols += [c for c in req['items']]
159     if req['items_available'] < offset+page:
160         break
161     offset += page
162     req = api.collections().list(filters=filters, select=['uuid', 'properties'], limit=page, offset=offset).execute()
163
164 print("Found {} collections".format(len(cols)))
165 for c in cols:
166     print("Updating collection {}".format(c["uuid"]))
167     props = c['properties']
168     props['responsible_person_uuid'] = new_uuid
169     api.collections().update(uuid=c['uuid'], body={'properties': props}).execute()
170 {% endcodeblock %}
171