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: * Contructs 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:
55: $this->url = $this->getParent()->url('metadata');
56: $this->key = $key;
57:
58: // in either case, retrieve the data
59: $response = $this->getParent()
60: ->getClient()
61: ->get($this->getUrl())
62: ->send();
63:
64: // parse and assign the server metadata
65: $body = Formatter::decode($response);
66:
67: if (isset($body->metadata)) {
68: foreach ($body->metadata as $key => $value) {
69: $this->$key = $value;
70: }
71: }
72: }
73: }
74:
75: public function getParent()
76: {
77: return $this->parent;
78: }
79:
80: public function setParent($parent)
81: {
82: $this->parent = $parent;
83:
84: return $this;
85: }
86:
87: /**
88: * Returns the URL of the metadata (key or block)
89: *
90: * @return string
91: * @param string $subresource not used; required for strict compatibility
92: * @throws ServerUrlerror
93: */
94: public function getUrl($path = null, array $query = array())
95: {
96: if (!isset($this->url)) {
97: throw new Exceptions\ServerUrlError(
98: 'Metadata has no URL (new object)'
99: );
100: }
101:
102: return Url::factory($this->url)->addPath($this->key);
103: }
104:
105: /**
106: * Sets a new metadata value or block
107: *
108: * Note that, if you're setting a block, the block specified will
109: * *entirely replace* the existing block.
110: *
111: * @api
112: * @return void
113: * @throws MetadataCreateError
114: */
115: public function create()
116: {
117: return $this->getParent()
118: ->getClient()
119: ->put($this->getUrl(), self::getJsonHeader(), $this->getMetadataJson())
120: ->send();
121: }
122:
123: /**
124: * Updates a metadata key or block
125: *
126: * @api
127: * @return void
128: * @throws MetadataUpdateError
129: */
130: public function update()
131: {
132: return $this->getParent()
133: ->getClient()
134: ->post($this->getUrl(), self::getJsonHeader(), $this->getMetadataJson())
135: ->send();
136: }
137:
138: /**
139: * Deletes a metadata key or block
140: *
141: * @api
142: * @return void
143: * @throws MetadataDeleteError
144: */
145: public function delete()
146: {
147: return $this->getParent()->getClient()->delete($this->getUrl(), array());
148: }
149:
150: public function __set($key, $value)
151: {
152: // if a key was supplied when creating the object, then we can't set
153: // any other values
154: if ($this->key && $key != $this->key) {
155: throw new Exceptions\MetadataKeyError(sprintf(
156: Lang::translate('You cannot set extra values on [%s]'),
157: $this->getUrl()
158: ));
159: }
160:
161: // otherwise, just set it;
162: parent::__set($key, $value);
163: }
164:
165: /**
166: * Builds a metadata JSON string
167: *
168: * @return string
169: * @throws MetadataJsonError
170: * @codeCoverageIgnore
171: */
172: private function getMetadataJson()
173: {
174: $object = (object) array(
175: 'meta' => (object) array(),
176: 'metadata' => (object) array()
177: );
178:
179: // different element if only a key is set
180: if ($name = $this->key) {
181: $object->meta->$name = $this->$name;
182: } else {
183: $object->metadata = new \stdClass();
184: foreach ($this->keylist() as $key) {
185: $object->metadata->$key = (string) $this->$key;
186: }
187: }
188:
189: $json = json_encode($object);
190: $this->checkJsonError();
191:
192: return $json;
193: }
194: }
195: