~ubuntu-branches/ubuntu/precise/landscape-client/precise-updates

« back to all changes in this revision

Viewing changes to landscape/message_schemas.py

  • Committer: Package Import Robot
  • Author(s): Christopher Glass (Canonical)
  • Date: 2014-12-15 09:24:30 UTC
  • mfrom: (1.2.13)
  • Revision ID: package-import@ubuntu.com-20141215092430-z2f5btcgh00nzdal
Tags: 14.12-0ubuntu0.12.04
* New upstream version (LP: #1401523):
  - Fix regression occurring when performing Landscape-driven release
    upgrades (LP: #1389686)
  - Fix regression occurring when switching the client between different
    Landscape servers (LP: #1376134)
  - Support reporting QEMU virtualization (LP: #1374501)
  - Bump Juju integration message format (LP: #1369635, LP: #1362506)
  - Drop provisioning registration message (LP: #1344054)
  - Drop cloud registration message (LP: #1342646)
  - Fix handling broken packages (LP: #1326940)
  - Add new Swift usage message type (LP: #1320236)
  - Fix platform detection on POWER machines (LP: #1271615)
  - Fix platform detection for arm64 machines (LP: #1306824)
  - Added a mechanism to set the client's user-agent (LP: #1399139)
  - Fixed release-upgrader not asking for a seesion ID before attempting to
    send a message (LP: #1401867)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
from landscape.schema import (
2
2
    Message, KeyDict, Dict, List, Tuple,
3
 
    Bool, Int, Float, String, Unicode, UnicodeOrString, Constant,
4
 
    Any)
 
3
    Bool, Int, Float, Bytes, Unicode, Constant, Any)
5
4
 
6
5
# When adding a new schema, which deprecates an older schema, the recommended
7
6
# naming convention, is to name it SCHEMA_NAME_ and the last API version that
10
9
# i.e. if I have USERS and I'm deprecating it, in API 2.2, then USERS becomes
11
10
# USERS_2_1
12
11
 
13
 
utf8 = UnicodeOrString("utf-8")
14
12
 
15
13
process_info = KeyDict({"pid": Int(),
16
 
                        "name": utf8,
17
 
                        "state": String(),
 
14
                        "name": Unicode(),
 
15
                        "state": Bytes(),
18
16
                        "sleep-average": Int(),
19
17
                        "uid": Int(),
20
18
                        "gid": Int(),
53
51
    {"operation-id": Int(),
54
52
     "status": Int(),
55
53
     "result-code": Int(),
56
 
     "result-text": utf8},
 
54
     "result-text": Unicode()},
57
55
    optional=["result-code", "result-text"])
58
56
 
59
 
#ACTION_INFO is obsolete.
60
 
ACTION_INFO = Message(
61
 
    "action-info",
62
 
    {"response-id": Int(),
63
 
     "success": Bool(),
64
 
     "kind": String(),
65
 
     "parameters": String()})
66
 
 
67
57
COMPUTER_INFO = Message(
68
58
    "computer-info",
69
 
    {"hostname": utf8,
 
59
    {"hostname": Unicode(),
70
60
     "total-memory": Int(),
71
61
     "total-swap": Int(),
72
 
     "annotations": Dict(utf8, utf8)},
 
62
     "annotations": Dict(Unicode(), Unicode())},
73
63
    # Not sure why these are all optional, but it's explicitly tested
74
64
    # in the server
75
65
    optional=["hostname", "total-memory", "total-swap", "annotations"])
76
66
 
77
67
DISTRIBUTION_INFO = Message(
78
68
    "distribution-info",
79
 
    {"distributor-id": utf8,
80
 
     "description": utf8,
81
 
     "release": utf8,
82
 
     "code-name": utf8},
 
69
    {"distributor-id": Unicode(),
 
70
     "description": Unicode(),
 
71
     "release": Unicode(),
 
72
     "code-name": Unicode()},
83
73
    # all optional because the lsb-release file may not have all data.
84
74
    optional=["distributor-id", "description", "release", "code-name"])
85
75
 
 
76
CLOUD_METADATA = Message(
 
77
    "cloud-instance-metadata",
 
78
    {"instance-id": Unicode(),
 
79
     "ami-id": Unicode(),
 
80
     "instance-type": Unicode()})
 
81
 
86
82
 
87
83
hal_data = Dict(Unicode(),
88
84
                Any(Unicode(), List(Unicode()), Bool(), Int(), Float()))
101
97
 
102
98
 
103
99
HARDWARE_INFO = Message("hardware-info", {
104
 
    "data": utf8})
105
 
 
 
100
    "data": Unicode()})
 
101
 
 
102
juju_data = {"environment-uuid": Unicode(),
 
103
             "api-addresses": List(Unicode()),
 
104
             "unit-name": Unicode(),
 
105
             "private-address": Unicode()}
 
106
 
 
107
# The copy of juju_data is needed because Message mutates the dictionary
 
108
JUJU_UNITS_INFO = Message("juju-units-info", {
 
109
    "juju-info-list": List(KeyDict(juju_data.copy(),
 
110
                                   optional=["private-address"]))
 
111
    })
106
112
 
107
113
LOAD_AVERAGE = Message("load-average", {
108
114
    "load-averages": List(Tuple(Int(), Float())),
113
119
    })
114
120
 
115
121
CEPH_USAGE = Message("ceph-usage", {
116
 
    "ceph-usages": List(Tuple(Int(), Float())),
117
 
    "ring-id": utf8,
118
 
    })
 
122
    "ring-id": Unicode(),
 
123
    # Usage data points in the form (timestamp, size, avail, used)
 
124
    "data-points": List(Tuple(Int(), Int(), Int(), Int())),
 
125
    # Unused now, for backwards compatibility
 
126
    "ceph-usages": List(None)})
119
127
 
120
128
SWIFT_DEVICE_INFO = Message("swift-device-info", {
121
 
    "swift-device-info": List(KeyDict({"device": utf8, "mounted": Bool()}))
 
129
    "swift-device-info": List(
 
130
        KeyDict({"device": Unicode(), "mounted": Bool()}))
122
131
    })
123
132
 
 
133
SWIFT_USAGE = Message("swift-usage", {
 
134
    # Usage data points in the form (timestamp, device, size, avail, used)
 
135
    "data-points": List(Tuple(Int(), Unicode(), Int(), Int(), Int()))})
 
136
 
124
137
KEYSTONE_TOKEN = Message("keystone-token", {
125
 
    "data": Any(String(), Constant(None))
 
138
    "data": Any(Bytes(), Constant(None))
126
139
})
127
140
 
128
141
CHANGE_HA_SERVICE = Message(
129
142
    "change-ha-service",
130
 
    {"service-name": String(),  # keystone
131
 
     "unit-name": String(),     # keystone-9
132
 
     "state": String()})        # online or standby
 
143
    {"service-name": Bytes(),  # keystone
 
144
     "unit-name": Bytes(),     # keystone-9
 
145
     "state": Bytes()})        # online or standby
133
146
 
134
147
MEMORY_INFO = Message("memory-info", {
135
148
    "memory-info": List(Tuple(Float(), Int(), Int())),
143
156
    optional=["operation-id"])
144
157
 
145
158
MOUNT_ACTIVITY = Message("mount-activity", {
146
 
    "activities": List(Tuple(Float(), utf8, Bool()))})
 
159
    "activities": List(Tuple(Float(), Unicode(), Bool()))})
147
160
 
148
161
 
149
162
MOUNT_INFO = Message("mount-info", {
150
163
    "mount-info": List(Tuple(Float(),
151
 
                             KeyDict({"mount-point": utf8,
152
 
                                      "device": utf8,
153
 
                                      "filesystem": utf8,
 
164
                             KeyDict({"mount-point": Unicode(),
 
165
                                      "device": Unicode(),
 
166
                                      "filesystem": Unicode(),
154
167
                                      "total-space": Int()})
155
168
                             )),
156
169
    })
157
170
 
158
171
FREE_SPACE = Message("free-space", {
159
 
    "free-space": List(Tuple(Float(), utf8, Int()))})
 
172
    "free-space": List(Tuple(Float(), Unicode(), Int()))})
160
173
 
161
174
 
162
175
REGISTER = Message(
164
177
    # The term used in the UI is actually 'registration_key', but we keep
165
178
    # the message schema field as 'registration_password' in case a new
166
179
    # client contacts an older server.
167
 
    {"registration_password": Any(utf8, Constant(None)),
168
 
     "computer_title": utf8,
169
 
     "hostname": utf8,
170
 
     "account_name": utf8,
171
 
     "tags": Any(utf8, Constant(None)),
172
 
     "vm-info": String()},
173
 
    # hostname wasn't around in old versions
174
 
    optional=["registration_password", "hostname", "tags", "vm-info"])
175
 
 
176
 
 
 
180
    {"registration_password": Any(Unicode(), Constant(None)),
 
181
     "computer_title": Unicode(),
 
182
     "hostname": Unicode(),
 
183
     "account_name": Unicode(),
 
184
     "tags": Any(Unicode(), Constant(None)),
 
185
     "vm-info": Bytes(),
 
186
     "container-info": Unicode(),
 
187
     "access_group": Unicode()},
 
188
    optional=["registration_password", "hostname", "tags", "vm-info",
 
189
              "container-info", "access_group"])
 
190
 
 
191
 
 
192
REGISTER_3_3 = Message(
 
193
    "register",
 
194
    # The term used in the UI is actually 'registration_key', but we keep
 
195
    # the message schema field as 'registration_password' in case a new
 
196
    # client contacts an older server.
 
197
    {"registration_password": Any(Unicode(), Constant(None)),
 
198
     "computer_title": Unicode(),
 
199
     "hostname": Unicode(),
 
200
     "account_name": Unicode(),
 
201
     "tags": Any(Unicode(), Constant(None)),
 
202
     "vm-info": Bytes(),
 
203
     "container-info": Unicode(),
 
204
     "juju-info": KeyDict({"environment-uuid": Unicode(),
 
205
                           "api-addresses": List(Unicode()),
 
206
                           "machine-id": Unicode()}),
 
207
     "access_group": Unicode()},
 
208
    api="3.3",
 
209
    optional=["registration_password", "hostname", "tags", "vm-info",
 
210
              "container-info", "access_group", "juju-info"])
 
211
 
 
212
 
 
213
# XXX The register-provisioned-machine message is obsolete, it's kept around
 
214
# just to not break older LDS releases that import it (the last LDS release
 
215
# to have it is 14.07). Eventually it shall be dropped.
177
216
REGISTER_PROVISIONED_MACHINE = Message(
178
217
    "register-provisioned-machine",
179
 
    {"otp": String()})
180
 
 
181
 
 
 
218
    {"otp": Bytes()})
 
219
 
 
220
 
 
221
# XXX The register-cloud-vm message is obsolete, it's kept around just to not
 
222
# break older LDS releases that import it (the last LDS release to have it
 
223
# is 14.07). Eventually it shall be dropped.
182
224
REGISTER_CLOUD_VM = Message(
183
225
    "register-cloud-vm",
184
 
    {"hostname": utf8,
185
 
     "otp": Any(String(), Constant(None)),
 
226
    {"hostname": Unicode(),
 
227
     "otp": Any(Bytes(), Constant(None)),
186
228
     "instance_key": Unicode(),
187
 
     "account_name": Any(utf8, Constant(None)),
188
 
     "registration_password": Any(utf8, Constant(None)),
 
229
     "account_name": Any(Unicode(), Constant(None)),
 
230
     "registration_password": Any(Unicode(), Constant(None)),
189
231
     "reservation_key": Unicode(),
190
232
     "public_hostname": Unicode(),
191
233
     "local_hostname": Unicode(),
193
235
     "ramdisk_key": Any(Unicode(), Constant(None)),
194
236
     "launch_index": Int(),
195
237
     "image_key": Unicode(),
196
 
     "tags": Any(utf8, Constant(None)),
197
 
     "vm-info": String(),
 
238
     "tags": Any(Unicode(), Constant(None)),
 
239
     "vm-info": Bytes(),
198
240
     "public_ipv4": Unicode(),
199
 
     "local_ipv4": Unicode()},
200
 
     optional=["tags", "vm-info", "public_ipv4", "local_ipv4"])
 
241
     "local_ipv4": Unicode(),
 
242
     "access_group": Unicode()},
 
243
    optional=["tags", "vm-info", "public_ipv4", "local_ipv4", "access_group"])
 
244
 
201
245
 
202
246
TEMPERATURE = Message("temperature", {
203
 
    "thermal-zone": utf8,
 
247
    "thermal-zone": Unicode(),
204
248
    "temperatures": List(Tuple(Int(), Float())),
205
249
    })
206
250
 
207
251
PROCESSOR_INFO = Message(
208
252
    "processor-info",
209
253
    {"processors": List(KeyDict({"processor-id": Int(),
210
 
                                 "vendor": utf8,
211
 
                                 "model": utf8,
 
254
                                 "vendor": Unicode(),
 
255
                                 "model": Unicode(),
212
256
                                 "cache-size": Int(),
213
257
                                 },
214
 
                                optional=["vendor", "cache-size"])),
215
 
    })
 
258
                                optional=["vendor", "cache-size"]))})
216
259
 
217
260
user_data = KeyDict({
218
261
    "uid": Int(),
219
 
    "username": utf8,
220
 
    "name": Any(utf8, Constant(None)),
 
262
    "username": Unicode(),
 
263
    "name": Any(Unicode(), Constant(None)),
221
264
    "enabled": Bool(),
222
 
    "location": Any(utf8, Constant(None)),
223
 
    "home-phone": Any(utf8, Constant(None)),
224
 
    "work-phone": Any(utf8, Constant(None)),
 
265
    "location": Any(Unicode(), Constant(None)),
 
266
    "home-phone": Any(Unicode(), Constant(None)),
 
267
    "work-phone": Any(Unicode(), Constant(None)),
225
268
    "primary-gid": Any(Int(), Constant(None)),
226
 
    "primary-groupname": utf8},
 
269
    "primary-groupname": Unicode()},
227
270
    optional=["primary-groupname", "primary-gid"])
228
271
 
229
272
group_data = KeyDict({
230
273
    "gid": Int(),
231
 
    "name": utf8})
 
274
    "name": Unicode()})
232
275
 
233
276
USERS = Message(
234
277
    "users",
235
278
    {"operation-id": Int(),
236
279
     "create-users": List(user_data),
237
280
     "update-users": List(user_data),
238
 
     "delete-users": List(utf8),
 
281
     "delete-users": List(Unicode()),
239
282
     "create-groups": List(group_data),
240
283
     "update-groups": List(group_data),
241
 
     "delete-groups": List(utf8),
242
 
     "create-group-members": Dict(utf8, List(utf8)),
243
 
     "delete-group-members": Dict(utf8, List(utf8)),
 
284
     "delete-groups": List(Unicode()),
 
285
     "create-group-members": Dict(Unicode(), List(Unicode())),
 
286
     "delete-group-members": Dict(Unicode(), List(Unicode())),
244
287
     },
245
288
    # operation-id is only there for responses, and all other are
246
289
    # optional as long as one of them is there (no way to say that yet)
284
327
              "create-groups", "update-groups", "delete-groups",
285
328
              "create-group-members", "delete-group-members"])
286
329
 
287
 
opt_str = Any(utf8, Constant(None))
 
330
opt_str = Any(Unicode(), Constant(None))
288
331
OLD_USERS = Message(
289
332
    "users",
290
 
    {"users": List(KeyDict({"username": utf8,
 
333
    {"users": List(KeyDict({"username": Unicode(),
291
334
                            "uid": Int(),
292
335
                            "realname": opt_str,
293
336
                            "location": opt_str,
296
339
                            "enabled": Bool()},
297
340
                           optional=["location", "home-phone", "work-phone"])),
298
341
     "groups": List(KeyDict({"gid": Int(),
299
 
                             "name": utf8,
300
 
                             "members": List(utf8)}))},
 
342
                             "name": Unicode(),
 
343
                             "members": List(Unicode())}))},
301
344
    optional=["groups"])
302
345
 
303
346
package_ids_or_ranges = List(Any(Tuple(Int(), Int()), Int()))
315
358
              "not-available", "not-installed", "not-available-upgrades",
316
359
              "not-locked"])
317
360
 
318
 
package_locks = List(Tuple(utf8, utf8, utf8))
 
361
package_locks = List(Tuple(Unicode(), Unicode(), Unicode()))
319
362
PACKAGE_LOCKS = Message(
320
363
    "package-locks",
321
364
    {"created": package_locks,
324
367
 
325
368
CHANGE_PACKAGE_HOLDS = Message(
326
369
    "change-package-holds",
327
 
    {"created": List(utf8),
328
 
     "deleted": List(utf8)},
 
370
    {"created": List(Unicode()),
 
371
     "deleted": List(Unicode())},
329
372
    optional=["created", "deleted"])
330
373
 
331
374
CHANGE_PACKAGES_RESULT = Message(
334
377
     "must-install": List(Any(Int(), Constant(None))),
335
378
     "must-remove": List(Any(Int(), Constant(None))),
336
379
     "result-code": Int(),
337
 
     "result-text": utf8},
 
380
     "result-text": Unicode()},
338
381
    optional=["result-text", "must-install", "must-remove"])
339
382
 
340
383
UNKNOWN_PACKAGE_HASHES = Message("unknown-package-hashes", {
341
 
    "hashes": List(String()),
 
384
    "hashes": List(Bytes()),
342
385
    "request-id": Int(),
343
386
    })
344
387
 
345
388
PACKAGE_REPORTER_RESULT = Message("package-reporter-result", {
346
389
    "code": Int(),
347
 
    "err": utf8})
 
390
    "err": Unicode()})
348
391
 
349
392
ADD_PACKAGES = Message("add-packages", {
350
 
    "packages": List(KeyDict({"name": utf8,
 
393
    "packages": List(KeyDict({"name": Unicode(),
351
394
                              "description": Unicode(),
352
395
                              "section": Unicode(),
353
 
                              "relations": List(Tuple(Int(), utf8)),
 
396
                              "relations": List(Tuple(Int(), Unicode())),
354
397
                              "summary": Unicode(),
355
398
                              "installed-size": Any(Int(), Constant(None)),
356
399
                              "size": Any(Int(), Constant(None)),
357
 
                              "version": utf8,
 
400
                              "version": Unicode(),
358
401
                              "type": Int(),
359
402
                              })),
360
403
    "request-id": Int(),
365
408
 
366
409
TEST = Message(
367
410
    "test",
368
 
    {"greeting": String(),
 
411
    {"greeting": Bytes(),
369
412
     "consistency-error": Bool(),
370
 
     "echo": String(),
 
413
     "echo": Bytes(),
371
414
     "sequence": Int()},
372
415
    optional=["greeting", "consistency-error", "echo", "sequence"])
373
416
 
374
417
# The tuples are timestamp, value
375
418
GRAPH_DATA = KeyDict({"values": List(Tuple(Float(), Float())),
376
419
                      "error": Unicode(),
377
 
                      "script-hash": String()})
 
420
                      "script-hash": Bytes()})
378
421
 
379
422
CUSTOM_GRAPH = Message("custom-graph", {
380
423
    "data": Dict(Int(), GRAPH_DATA)})
397
440
 
398
441
EUCALYPTUS_INFO = Message(
399
442
    "eucalyptus-info",
400
 
    {"basic_info": Dict(String(), Any(String(), Constant(None))),
401
 
     "walrus_info": String(),
402
 
     "cluster_controller_info": String(),
403
 
     "storage_controller_info": String(),
404
 
     "node_controller_info": String(),
405
 
     "capacity_info": String()},
 
443
    {"basic_info": Dict(Bytes(), Any(Bytes(), Constant(None))),
 
444
     "walrus_info": Bytes(),
 
445
     "cluster_controller_info": Bytes(),
 
446
     "storage_controller_info": Bytes(),
 
447
     "node_controller_info": Bytes(),
 
448
     "capacity_info": Bytes()},
406
449
    optional=["capacity_info"])
407
450
 
408
451
EUCALYPTUS_INFO_ERROR = Message(
409
452
    "eucalyptus-info-error",
410
 
    {"error": String()})
 
453
    {"error": Bytes()})
411
454
 
412
455
# The network-device message is split in two top level keys because we don't
413
456
# support adding sub-keys in a backwards-compatible way (only top-level keys).
415
458
# simply ignore the extra info..
416
459
NETWORK_DEVICE = Message(
417
460
    "network-device",
418
 
    {"devices": List(KeyDict({"interface": String(),
419
 
                              "ip_address": String(),
420
 
                              "mac_address": String(),
421
 
                              "broadcast_address": String(),
422
 
                              "netmask": String(),
 
461
    {"devices": List(KeyDict({"interface": Bytes(),
 
462
                              "ip_address": Bytes(),
 
463
                              "mac_address": Bytes(),
 
464
                              "broadcast_address": Bytes(),
 
465
                              "netmask": Bytes(),
423
466
                              "flags": Int()})),
424
467
 
425
 
     "device-speeds": List(KeyDict({"interface": String(),
 
468
     "device-speeds": List(KeyDict({"interface": Bytes(),
426
469
                                    "speed": Int(),
427
470
                                    "duplex": Bool()}))},
428
471
    optional=["device-speeds"])
434
477
    # an interface a is a list of 3-tuples (step, in, out), where 'step' is the
435
478
    # time interval and 'in'/'out' are number of bytes received/sent over the
436
479
    # interval.
437
 
    {"activities": Dict(String(), List(Tuple(Int(), Int(), Int())))})
438
 
 
439
 
UPDATE_MANAGER_INFO = Message("update-manager-info", {"prompt": utf8})
440
 
 
441
 
 
442
 
message_schemas = {}
443
 
for schema in [ACTIVE_PROCESS_INFO, COMPUTER_UPTIME, CLIENT_UPTIME,
444
 
               OPERATION_RESULT, COMPUTER_INFO, DISTRIBUTION_INFO,
445
 
               HARDWARE_INVENTORY, HARDWARE_INFO, LOAD_AVERAGE, MEMORY_INFO,
446
 
               RESYNCHRONIZE, MOUNT_ACTIVITY, MOUNT_INFO, FREE_SPACE,
447
 
               REGISTER, REGISTER_CLOUD_VM, REGISTER_PROVISIONED_MACHINE,
448
 
               TEMPERATURE, PROCESSOR_INFO, USERS, PACKAGES, PACKAGE_LOCKS,
449
 
               CHANGE_PACKAGES_RESULT, UNKNOWN_PACKAGE_HASHES,
450
 
               ADD_PACKAGES, PACKAGE_REPORTER_RESULT, TEXT_MESSAGE, TEST,
451
 
               CUSTOM_GRAPH, REBOOT_REQUIRED, APT_PREFERENCES, EUCALYPTUS_INFO,
452
 
               EUCALYPTUS_INFO_ERROR, NETWORK_DEVICE, NETWORK_ACTIVITY,
453
 
               REBOOT_REQUIRED_INFO, UPDATE_MANAGER_INFO, CPU_USAGE,
454
 
               CEPH_USAGE, SWIFT_DEVICE_INFO, KEYSTONE_TOKEN,
455
 
               CHANGE_HA_SERVICE]:
456
 
    message_schemas[schema.type] = schema
 
480
    {"activities": Dict(Bytes(), List(Tuple(Int(), Int(), Int())))})
 
481
 
 
482
UPDATE_MANAGER_INFO = Message("update-manager-info", {"prompt": Unicode()})
 
483
 
 
484
 
 
485
message_schemas = (
 
486
    ACTIVE_PROCESS_INFO, COMPUTER_UPTIME, CLIENT_UPTIME,
 
487
    OPERATION_RESULT, COMPUTER_INFO, DISTRIBUTION_INFO,
 
488
    HARDWARE_INVENTORY, HARDWARE_INFO, LOAD_AVERAGE, MEMORY_INFO,
 
489
    RESYNCHRONIZE, MOUNT_ACTIVITY, MOUNT_INFO, FREE_SPACE,
 
490
    REGISTER, REGISTER_3_3,
 
491
    TEMPERATURE, PROCESSOR_INFO, USERS, PACKAGES, PACKAGE_LOCKS,
 
492
    CHANGE_PACKAGES_RESULT, UNKNOWN_PACKAGE_HASHES,
 
493
    ADD_PACKAGES, PACKAGE_REPORTER_RESULT, TEXT_MESSAGE, TEST,
 
494
    CUSTOM_GRAPH, REBOOT_REQUIRED, APT_PREFERENCES,
 
495
    NETWORK_DEVICE, NETWORK_ACTIVITY,
 
496
    REBOOT_REQUIRED_INFO, UPDATE_MANAGER_INFO, CPU_USAGE,
 
497
    CEPH_USAGE, SWIFT_USAGE, SWIFT_DEVICE_INFO, KEYSTONE_TOKEN,
 
498
    CHANGE_HA_SERVICE, JUJU_UNITS_INFO, CLOUD_METADATA)