1: <?php
2: /**
3: * Copyright 2012-2014 Rackspace US, Inc.
4: *
5: * Licensed under the Apache License, Version 2.0 (the "License");
6: * you may not use this file except in compliance with the License.
7: * You may obtain a copy of the License at
8: *
9: * http://www.apache.org/licenses/LICENSE-2.0
10: *
11: * Unless required by applicable law or agreed to in writing, software
12: * distributed under the License is distributed on an "AS IS" BASIS,
13: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14: * See the License for the specific language governing permissions and
15: * limitations under the License.
16: */
17:
18: namespace OpenCloud\Compute\Resource;
19:
20: use Guzzle\Http\Url;
21: use OpenCloud\Common\Exceptions;
22: use OpenCloud\Common\Http\Message\Formatter;
23: use OpenCloud\Common\Lang;
24: use OpenCloud\Common\Metadata;
25:
26: /**
27: * This class handles specialized metadata for OpenStack Server objects (metadata
28: * items can be managed individually or in aggregate).
29: *
30: * Server metadata is a weird beast in that it has resource representations
31: * and HTTP calls to set the entire server metadata as well as individual
32: * items.
33: */
34: class ServerMetadata extends Metadata
35: {
36: private $parent;
37: protected $key; // the metadata item (if supplied)
38: private $url; // the URL of this particular metadata item or block
39:
40: /**
41: * Constructs a Metadata object associated with a Server or Image object
42: *
43: * @param object $parent either a Server or an Image object
44: * @param string $key the (optional) key for the metadata item
45: * @throws MetadataError
46: */
47: public function __construct(Server $parent, $key = null)
48: {
49: // construct defaults
50: $this->setParent($parent);
51:
52: // set the URL according to whether or not we have a key
53: if ($this->getParent()->getId()) {
54: $this->url = $this->getParent()->url('metadata');
55: $this->key = $key;
56:
57: // in either case, retrieve the data
58: $response = $this->getParent()
59: ->getClient()
60: ->get($this->getUrl())
61: ->send();
62:
63: // parse and assign the server metadata
64: $body = Formatter::decode($response);
65:
66: if (isset($body->metadata)) {
67: foreach ($body->metadata as $key => $value) {
68: $this->$key = $value;
69: }
70: }
71: }
72: }
73:
74: public function getParent()
75: {
76: return $this->parent;
77: }
78:
79: public function setParent($parent)
80: {
81: $this->parent = $parent;
82:
83: return $this;
84: }
85:
86: /**
87: * Returns the URL of the metadata (key or block)
88: *
89: * @return string
90: * @param string $subresource not used; required for strict compatibility
91: * @throws ServerUrlerror
92: */
93: public function getUrl($path = null, array $query = array())
94: {
95: if (!isset($this->url)) {
96: throw new Exceptions\ServerUrlError(
97: 'Metadata has no URL (new object)'
98: );
99: }
100:
101: return Url::factory($this->url)->addPath($this->key);
102: }
103:
104: /**
105: * Sets a new metadata value or block
106: *
107: * Note that, if you're setting a block, the block specified will
108: * *entirely replace* the existing block.
109: *
110: * @api
111: * @return void
112: * @throws MetadataCreateError
113: */
114: public function create()
115: {
116: return $this->getParent()
117: ->getClient()
118: ->put($this->getUrl(), self::getJsonHeader(), $this->getMetadataJson())
119: ->send();
120: }
121:
122: /**
123: * Updates a metadata key or block
124: *
125: * @api
126: * @return void
127: * @throws MetadataUpdateError
128: */
129: public function update()
130: {
131: return $this->getParent()
132: ->getClient()
133: ->post($this->getUrl(), self::getJsonHeader(), $this->getMetadataJson())
134: ->send();
135: }
136:
137: /**
138: * Deletes a metadata key or block
139: *
140: * @api
141: * @return void
142: * @throws MetadataDeleteError
143: */
144: public function delete()
145: {
146: return $this->getParent()->getClient()->delete($this->getUrl(), array());
147: }
148:
149: public function __set($key, $value)
150: {
151: // if a key was supplied when creating the object, then we can't set
152: // any other values
153: if ($this->key && $key != $this->key) {
154: throw new Exceptions\MetadataKeyError(sprintf(
155: Lang::translate('You cannot set extra values on [%s]'),
156: $this->getUrl()
157: ));
158: }
159:
160: // otherwise, just set it;
161: parent::__set($key, $value);
162: }
163:
164: /**
165: * Builds a metadata JSON string
166: *
167: * @return string
168: * @throws MetadataJsonError
169: * @codeCoverageIgnore
170: */
171: private function getMetadataJson()
172: {
173: $object = (object) array(
174: 'meta' => (object) array(),
175: 'metadata' => (object) array()
176: );
177:
178: // different element if only a key is set
179: if ($name = $this->key) {
180: $object->meta->$name = $this->$name;
181: } else {
182: $object->metadata = new \stdClass();
183: foreach ($this->keylist() as $key) {
184: $object->metadata->$key = (string) $this->$key;
185: }
186: }
187:
188: $json = json_encode($object);
189: $this->checkJsonError();
190:
191: return $json;
192: }
193: }
194: