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