occi-core 4.1.3 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +8 -0
- data/README.md +49 -17
- data/lib/occi/collection.rb +37 -21
- data/lib/occi/core/action.rb +5 -5
- data/lib/occi/core/action_instance.rb +45 -3
- data/lib/occi/core/actions.rb +2 -1
- data/lib/occi/core/attributes.rb +253 -73
- data/lib/occi/core/categories.rb +1 -0
- data/lib/occi/core/category.rb +25 -8
- data/lib/occi/core/entities.rb +1 -0
- data/lib/occi/core/entity.rb +51 -74
- data/lib/occi/core/kind.rb +15 -11
- data/lib/occi/core/kinds.rb +1 -1
- data/lib/occi/core/link.rb +14 -15
- data/lib/occi/core/links.rb +1 -1
- data/lib/occi/core/mixin.rb +5 -5
- data/lib/occi/core/mixins.rb +2 -2
- data/lib/occi/core/properties.rb +90 -12
- data/lib/occi/core/resource.rb +7 -3
- data/lib/occi/core/resources.rb +2 -2
- data/lib/occi/errors/attribute_definitions_converted_error.rb +5 -0
- data/lib/occi/errors/attribute_missing_error.rb +5 -0
- data/lib/occi/errors/attribute_name_invalid_error.rb +5 -0
- data/lib/occi/errors/attribute_not_defined_error.rb +5 -0
- data/lib/occi/errors/attribute_property_type_error.rb +5 -0
- data/lib/occi/errors/attribute_type_error.rb +5 -0
- data/lib/occi/errors/kind_not_defined_error.rb +5 -0
- data/lib/occi/errors/parser_input_error.rb +5 -0
- data/lib/occi/errors/parser_type_error.rb +5 -0
- data/lib/occi/errors.rb +1 -0
- data/lib/occi/extensions/hashie.rb +25 -0
- data/lib/occi/helpers/comparators/action_instance.rb +22 -0
- data/lib/occi/helpers/comparators/attributes.rb +22 -0
- data/lib/occi/helpers/comparators/categories.rb +22 -0
- data/lib/occi/helpers/comparators/category.rb +22 -0
- data/lib/occi/helpers/comparators/collection.rb +40 -0
- data/lib/occi/helpers/comparators/entities.rb +22 -0
- data/lib/occi/helpers/comparators/entity.rb +22 -0
- data/lib/occi/helpers/comparators/properties.rb +26 -0
- data/lib/occi/helpers/comparators.rb +1 -0
- data/lib/occi/infrastructure/compute.rb +11 -9
- data/lib/occi/infrastructure/network.rb +27 -27
- data/lib/occi/infrastructure/networkinterface.rb +22 -23
- data/lib/occi/infrastructure/os_tpl.rb +1 -1
- data/lib/occi/infrastructure/resource_tpl.rb +1 -1
- data/lib/occi/infrastructure/storage.rb +7 -6
- data/lib/occi/infrastructure/storagelink.rb +4 -4
- data/lib/occi/log.rb +13 -10
- data/lib/occi/model.rb +9 -8
- data/lib/occi/parser/json.rb +11 -9
- data/lib/occi/parser/ova.rb +12 -6
- data/lib/occi/parser/ovf.rb +173 -116
- data/lib/occi/parser/text/constants.rb +87 -0
- data/lib/occi/parser/text.rb +161 -200
- data/lib/occi/parser/xml.rb +10 -8
- data/lib/occi/parser.rb +100 -50
- data/lib/occi/settings.rb +2 -1
- data/lib/occi/version.rb +1 -1
- data/lib/occi-core.rb +6 -4
- data/occi-core.gemspec +0 -7
- data/spec/occi/collection_samples/collection1.json +1 -0
- data/spec/occi/collection_samples/directory2/collection2.json +1 -0
- data/spec/occi/collection_spec.rb +961 -31
- data/spec/occi/core/action_instance_spec.rb +317 -0
- data/spec/occi/core/action_spec.rb +71 -0
- data/spec/occi/core/attributes_spec.rb +582 -27
- data/spec/occi/core/category_spec.rb +194 -18
- data/spec/occi/core/entities_spec.rb +96 -0
- data/spec/occi/core/entity_spec.rb +317 -28
- data/spec/occi/core/kind_spec.rb +127 -16
- data/spec/occi/core/link_spec.rb +35 -0
- data/spec/occi/core/links_spec.rb +130 -0
- data/spec/occi/core/mixins_spec.rb +107 -0
- data/spec/occi/core/properties_spec.rb +167 -0
- data/spec/occi/core/resource_spec.rb +23 -9
- data/spec/occi/core_spec.rb +12 -0
- data/spec/occi/infrastructure/compute_spec.rb +218 -18
- data/spec/occi/infrastructure/network_spec.rb +96 -0
- data/spec/occi/infrastructure/networkinterface_spec.rb +96 -0
- data/spec/occi/infrastructure/storage_spec.rb +33 -0
- data/spec/occi/infrastructure/storagelink_spec.rb +45 -0
- data/spec/occi/log_spec.rb +104 -1
- data/spec/occi/model_spec.rb +251 -39
- data/spec/occi/{test.json → parser/json_samples/test.json} +0 -0
- data/spec/occi/parser/ova_samples/test.dump +0 -0
- data/spec/occi/{test.ova → parser/ova_samples/test.ova} +0 -0
- data/spec/occi/parser/ovf_samples/test.dump +0 -0
- data/spec/occi/{test.ovf → parser/ovf_samples/test.ovf} +0 -0
- data/spec/occi/parser/text_samples/occi_categories.dump +0 -0
- data/spec/occi/parser/text_samples/occi_categories.text +2 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.text +10 -0
- data/spec/occi/parser/text_samples/occi_link_resource_instance.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_resource_instance.text +7 -0
- data/spec/occi/parser/text_samples/occi_link_simple.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_simple.link_string.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_simple.text +1 -0
- data/spec/occi/parser/text_samples/occi_link_w_attributes.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_w_attributes.text +7 -0
- data/spec/occi/parser/text_samples/occi_link_w_category.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_w_category.text +3 -0
- data/spec/occi/parser/text_samples/occi_model_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_model_rocci_server.text +51 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.text +11 -0
- data/spec/occi/parser/text_samples/occi_resource_w_attributes.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_attributes.text +11 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links.text +16 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.text +13 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.text +9 -0
- data/spec/occi/parser/text_spec.rb +274 -78
- data/spec/occi/parser/xml_samples/test.xml +352 -0
- data/spec/occi/parser_spec.rb +255 -104
- data/spec/occi-core_spec.rb +31 -0
- data/spec/spec_helper.rb +6 -2
- metadata +110 -111
- checksums.yaml +0 -7
- data/spec/occi/core/attribute_spec.rb +0 -0
data/lib/occi/parser/text.rb
CHANGED
|
@@ -1,250 +1,211 @@
|
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'text', '*.rb')].each { |file| require file.gsub('.rb', '') }
|
|
2
|
+
|
|
1
3
|
module Occi
|
|
2
4
|
module Parser
|
|
3
5
|
module Text
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
REGEXP_QUOTED_STRING = /([^"\\]|\\.)*/
|
|
7
|
-
REGEXP_LOALPHA = /[a-z]/
|
|
8
|
-
REGEXP_DIGIT = /[0-9]/
|
|
9
|
-
REGEXP_INT = /#{REGEXP_DIGIT}+/
|
|
10
|
-
REGEXP_FLOAT = /#{REGEXP_INT}\.#{REGEXP_INT}/
|
|
11
|
-
REGEXP_NUMBER = /#{REGEXP_FLOAT}|#{REGEXP_INT}/
|
|
12
|
-
REGEXP_BOOL = /true|false/
|
|
13
|
-
|
|
14
|
-
# Regular expressions for OCCI
|
|
15
|
-
if Occi::Settings.compatibility
|
|
16
|
-
# Compatibility with terms starting with a number
|
|
17
|
-
REGEXP_TERM = /(#{REGEXP_LOALPHA}|#{REGEXP_DIGIT})(#{REGEXP_LOALPHA}|#{REGEXP_DIGIT}|-|_)*/
|
|
18
|
-
else
|
|
19
|
-
REGEXP_TERM = /#{REGEXP_LOALPHA}(#{REGEXP_LOALPHA}|#{REGEXP_DIGIT}|-|_)*/
|
|
20
|
-
end
|
|
21
|
-
REGEXP_SCHEME = /#{URI::ABS_URI_REF}#/
|
|
22
|
-
REGEXP_TYPE_IDENTIFIER = /#{REGEXP_SCHEME}#{REGEXP_TERM}/
|
|
23
|
-
REGEXP_CLASS = /action|mixin|kind/
|
|
24
|
-
|
|
25
|
-
REGEXP_ATTR_COMPONENT = /#{REGEXP_LOALPHA}(#{REGEXP_LOALPHA}|#{REGEXP_DIGIT}|-|_)*/
|
|
26
|
-
REGEXP_ATTRIBUTE_NAME = /#{REGEXP_ATTR_COMPONENT}(\.#{REGEXP_ATTR_COMPONENT})*/
|
|
27
|
-
REGEXP_ATTRIBUTE_PROPERTY = /immutable|required/
|
|
28
|
-
REGEXP_ATTRIBUTE_DEF = /(#{REGEXP_ATTRIBUTE_NAME})(\{#{REGEXP_ATTRIBUTE_PROPERTY}(\s+#{REGEXP_ATTRIBUTE_PROPERTY})*\})?/
|
|
29
|
-
REGEXP_ATTRIBUTE_LIST = /#{REGEXP_ATTRIBUTE_DEF}(\s+#{REGEXP_ATTRIBUTE_DEF})*/
|
|
30
|
-
REGEXP_ATTRIBUTE_REPR = /#{REGEXP_ATTRIBUTE_NAME}=("#{REGEXP_QUOTED_STRING}"|#{REGEXP_NUMBER}|#{REGEXP_BOOL})/
|
|
31
|
-
|
|
32
|
-
REGEXP_ACTION = /#{REGEXP_TYPE_IDENTIFIER}/
|
|
33
|
-
REGEXP_ACTION_LIST = /#{REGEXP_ACTION}(\s+#{REGEXP_ACTION})*/
|
|
34
|
-
|
|
35
|
-
REGEXP_RESOURCE_TYPE = /#{REGEXP_TYPE_IDENTIFIER}(\s+#{REGEXP_TYPE_IDENTIFIER})*/
|
|
36
|
-
REGEXP_LINK_INSTANCE = /#{URI::URI_REF}/
|
|
37
|
-
REGEXP_LINK_TYPE = /#{REGEXP_TYPE_IDENTIFIER}(\s+#{REGEXP_TYPE_IDENTIFIER})*/
|
|
38
|
-
|
|
39
|
-
# Regular expression for OCCI Categories
|
|
40
|
-
if Occi::Settings.compatibility
|
|
41
|
-
REGEXP_CATEGORY = "Category:\\s*(?<term>#{REGEXP_TERM})" << # term (mandatory)
|
|
42
|
-
";\\s*scheme=\"(?<scheme>#{REGEXP_SCHEME})#{REGEXP_TERM}?\"" << # scheme (mandatory)
|
|
43
|
-
";\\s*class=\"(?<class>#{REGEXP_CLASS})\"" << # class (mandatory)
|
|
44
|
-
"(;\\s*title=\"(?<title>#{REGEXP_QUOTED_STRING})\")?" << # title (optional)
|
|
45
|
-
"(;\\s*rel=\"(?<rel>#{REGEXP_TYPE_IDENTIFIER})\")?"<< # rel (optional)
|
|
46
|
-
"(;\\s*location=\"(?<location>#{URI::URI_REF})\")?" << # location (optional)
|
|
47
|
-
"(;\\s*attributes=\"(?<attributes>#{REGEXP_ATTRIBUTE_LIST})\")?" << # attributes (optional)
|
|
48
|
-
"(;\\s*actions=\"(?<actions>#{REGEXP_ACTION_LIST})\")?" << # actions (optional)
|
|
49
|
-
';?' # additional semicolon at the end (not specified, for interoperability)
|
|
50
|
-
else
|
|
51
|
-
REGEXP_CATEGORY = "Category:\\s*(?<term>#{REGEXP_TERM})" << # term (mandatory)
|
|
52
|
-
";\\s*scheme=\"(?<scheme>#{REGEXP_SCHEME})\"" << # scheme (mandatory)
|
|
53
|
-
";\\s*class=\"(?<class>#{REGEXP_CLASS})\"" << # class (mandatory)
|
|
54
|
-
"(;\\s*title=\"(?<title>#{REGEXP_QUOTED_STRING})\")?" << # title (optional)
|
|
55
|
-
"(;\\s*rel=\"(?<rel>#{REGEXP_TYPE_IDENTIFIER})\")?"<< # rel (optional)
|
|
56
|
-
"(;\\s*location=\"(?<location>#{URI::URI_REF})\")?" << # location (optional)
|
|
57
|
-
"(;\\s*attributes=\"(?<attributes>#{REGEXP_ATTRIBUTE_LIST})\")?" << # attributes (optional)
|
|
58
|
-
"(;\\s*actions=\"(?<actions>#{REGEXP_ACTION_LIST})\")?" << # actions (optional)
|
|
59
|
-
';?' # additional semicolon at the end (not specified, for interoperability)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Regular expression for OCCI Link Instance References
|
|
63
|
-
if Occi::Settings.compatibility
|
|
64
|
-
REGEXP_LINK = "Link:\\s*\\<(?<uri>#{URI::URI_REF})\\>" << # uri (mandatory)
|
|
65
|
-
";\\s*rel=\"(?<rel>#{REGEXP_RESOURCE_TYPE})\"" << # rel (mandatory)
|
|
66
|
-
"(;\\s*self=\"(?<self>#{REGEXP_LINK_INSTANCE})\")?" << # self (optional)
|
|
67
|
-
"(;\\s*category=\"(?<category>(;?\\s*(#{REGEXP_LINK_TYPE}))+)\")?" << # category (optional)
|
|
68
|
-
"(?<attributes>(;?\\s*(#{REGEXP_ATTRIBUTE_REPR}))*)" << # attributes (optional)
|
|
69
|
-
';?' # additional semicolon at the end (not specified, for interoperability)
|
|
70
|
-
else
|
|
71
|
-
REGEXP_LINK = "Link:\\s*\\<(?<uri>#{URI::URI_REF})\\>" << # uri (mandatory)
|
|
72
|
-
";\\s*rel=\"(?<rel>#{REGEXP_RESOURCE_TYPE})\"" << # rel (mandatory)
|
|
73
|
-
"(;\\s*self=\"(?<self>#{REGEXP_LINK_INSTANCE})\")?" << # self (optional)
|
|
74
|
-
"(;\\s*category=\"(?<category>(;?\\s*(#{REGEXP_LINK_TYPE}))+)\")?" << # category (optional)
|
|
75
|
-
"(?<attributes>(;\\s*(#{REGEXP_ATTRIBUTE_REPR}))*)" << # attributes (optional)
|
|
76
|
-
';?' # additional semicolon at the end (not specified, for interoperability)
|
|
77
|
-
end
|
|
7
|
+
class << self
|
|
78
8
|
|
|
79
|
-
|
|
80
|
-
REGEXP_ATTRIBUTE = "X-OCCI-Attribute:\\s*(?<name>#{REGEXP_ATTRIBUTE_NAME})=(\"(?<string>#{REGEXP_QUOTED_STRING})\"|(?<number>#{REGEXP_NUMBER})|(?<bool>#{REGEXP_BOOL}))" <<
|
|
81
|
-
';?' # additional semicolon at the end (not specified, for interoperability)
|
|
9
|
+
include Occi::Parser::Text::Constants
|
|
82
10
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
11
|
+
def categories(lines)
|
|
12
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.categories"
|
|
13
|
+
collection = Occi::Collection.new
|
|
86
14
|
|
|
15
|
+
block = Proc.new { |line|
|
|
16
|
+
line.strip!
|
|
17
|
+
category = category(line) if line.start_with? 'Category:'
|
|
18
|
+
collection << category if category.kind_of? Occi::Core::Category
|
|
19
|
+
}
|
|
87
20
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
lines.each do |line|
|
|
91
|
-
line.strip!
|
|
92
|
-
category = self.category(line) if line.start_with? 'Category:'
|
|
93
|
-
collection << category if category.kind_of? Occi::Core::Category
|
|
21
|
+
lines.respond_to?(:each) ? lines.each(&block) : lines.each_line(&block)
|
|
22
|
+
collection
|
|
94
23
|
end
|
|
95
|
-
collection
|
|
96
|
-
end
|
|
97
24
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
25
|
+
def resource(lines)
|
|
26
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.resource"
|
|
27
|
+
collection = Occi::Collection.new
|
|
28
|
+
resource = Occi::Core::Resource.new
|
|
29
|
+
resource.id = nil
|
|
30
|
+
links = []
|
|
31
|
+
|
|
32
|
+
block = Proc.new { |line|
|
|
33
|
+
line.strip!
|
|
34
|
+
case line
|
|
104
35
|
when /^Category:/
|
|
105
|
-
category =
|
|
106
|
-
|
|
36
|
+
category = category(line)
|
|
37
|
+
|
|
38
|
+
if category.kind_of? Occi::Core::Kind
|
|
39
|
+
resource = Occi::Core::Entity.new(category.type_identifier)
|
|
40
|
+
resource.kind = category
|
|
41
|
+
end
|
|
107
42
|
resource.mixins << category if category.kind_of? Occi::Core::Mixin
|
|
108
43
|
when /^X-OCCI-Attribute:/
|
|
109
|
-
resource.attributes.merge!
|
|
44
|
+
resource.attributes.merge! attribute(line)
|
|
110
45
|
when /^Link:/
|
|
111
|
-
link =
|
|
46
|
+
link = link_string(line, resource)
|
|
112
47
|
resource.links << link
|
|
113
|
-
|
|
48
|
+
links << link
|
|
49
|
+
end
|
|
50
|
+
}
|
|
51
|
+
lines.respond_to?(:each) ? lines.each(&block) : lines.each_line(&block)
|
|
52
|
+
|
|
53
|
+
if resource.kind_of?(Occi::Core::Resource) && !resource.empty?
|
|
54
|
+
collection << resource
|
|
55
|
+
links.each { |link| collection << link }
|
|
114
56
|
end
|
|
57
|
+
|
|
58
|
+
collection
|
|
115
59
|
end
|
|
116
|
-
collection << resource if resource.kind_of? Occi::Core::Resource
|
|
117
|
-
collection
|
|
118
|
-
end
|
|
119
60
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
61
|
+
def link(lines)
|
|
62
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.link"
|
|
63
|
+
collection = Occi::Collection.new
|
|
64
|
+
link = Occi::Core::Link.new
|
|
65
|
+
link.id = nil
|
|
66
|
+
|
|
67
|
+
block = Proc.new { |line|
|
|
68
|
+
line.strip!
|
|
69
|
+
case line
|
|
126
70
|
when /^Category:/
|
|
127
|
-
category =
|
|
71
|
+
category = category(line)
|
|
128
72
|
link.kind = category if category.kind_of? Occi::Core::Kind
|
|
129
73
|
link.mixins << category if category.kind_of? Occi::Core::Mixin
|
|
130
74
|
when /^X-OCCI-Attribute:/
|
|
131
|
-
link.attributes.merge!
|
|
132
|
-
|
|
75
|
+
link.attributes.merge! attribute(line)
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
lines.respond_to?(:each) ? lines.each(&block) : lines.each_line(&block)
|
|
79
|
+
|
|
80
|
+
collection << link if link.kind_of?(Occi::Core::Link) && !link.empty?
|
|
81
|
+
collection
|
|
133
82
|
end
|
|
134
|
-
collection << link if link.kind_of? Occi::Core::Link
|
|
135
|
-
collection
|
|
136
|
-
end
|
|
137
83
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
84
|
+
def locations(lines)
|
|
85
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.locations"
|
|
86
|
+
locations = []
|
|
87
|
+
|
|
88
|
+
block = Proc.new { |line|
|
|
89
|
+
line.strip!
|
|
90
|
+
locations << location(line) if line.start_with? 'X-OCCI-Location:'
|
|
91
|
+
}
|
|
92
|
+
lines.respond_to?(:each) ? lines.each(&block) : lines.each_line(&block)
|
|
93
|
+
|
|
94
|
+
locations
|
|
143
95
|
end
|
|
144
|
-
locations
|
|
145
|
-
end
|
|
146
96
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
97
|
+
def category(string)
|
|
98
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.category"
|
|
99
|
+
# create regular expression from regexp string
|
|
100
|
+
regexp = Regexp.new( Occi::Settings.compatibility ? REGEXP_CATEGORY : REGEXP_CATEGORY_STRICT )
|
|
101
|
+
# match string to regular expression
|
|
102
|
+
match = regexp.match string
|
|
103
|
+
|
|
104
|
+
raise Occi::Errors::ParserInputError, "could not match #{string}" unless match
|
|
105
|
+
|
|
106
|
+
term = match[:term].downcase
|
|
107
|
+
scheme = match[:scheme]
|
|
108
|
+
title = match[:title]
|
|
109
|
+
related = match[:rel].to_s.split
|
|
110
|
+
|
|
111
|
+
attributes = Occi::Core::Attributes.new
|
|
112
|
+
if match[:attributes]
|
|
113
|
+
match[:attributes].split.each do |attribute|
|
|
114
|
+
property_string = attribute[/#{REGEXP_ATTRIBUTE_DEF}/, -2]
|
|
115
|
+
properties = Occi::Core::Properties.new
|
|
116
|
+
|
|
117
|
+
if property_string
|
|
118
|
+
properties.required = true if property_string.include? 'required'
|
|
119
|
+
properties.mutable = false if property_string.include? 'immutable'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
name = attribute[/#{REGEXP_ATTRIBUTE_DEF}/, 1]
|
|
123
|
+
attributes.merge! name.split('.').reverse.inject(properties) { |a, n| Occi::Core::Attributes.new(n => a) }
|
|
171
124
|
end
|
|
172
|
-
name = attribute[/#{REGEXP_ATTRIBUTE_DEF}/, 1]
|
|
173
|
-
attributes.merge! name.split('.').reverse.inject(properties) { |a, n| Occi::Core::Attributes.new(n => a) }
|
|
174
125
|
end
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
126
|
+
actions = match[:actions].to_s.split
|
|
127
|
+
location = match[:location]
|
|
128
|
+
|
|
129
|
+
case match[:class]
|
|
179
130
|
when 'kind'
|
|
131
|
+
Occi::Log.debug "[#{self}] class #{match[:class]} identified as kind"
|
|
180
132
|
Occi::Core::Kind.new scheme, term, title, attributes, related, actions, location
|
|
181
133
|
when 'mixin'
|
|
134
|
+
Occi::Log.debug "[#{self}] class #{match[:class]} identified as mixin"
|
|
182
135
|
Occi::Core::Mixin.new scheme, term, title, attributes, related, actions, location
|
|
183
136
|
when 'action'
|
|
137
|
+
Occi::Log.debug "[#{self}] class #{match[:class]} identified as action"
|
|
184
138
|
Occi::Core::Action.new scheme, term, title, attributes
|
|
185
139
|
else
|
|
186
|
-
raise "Category with class #{match[:class]} not recognized in string: #{string}"
|
|
140
|
+
raise Occi::Errors::ParserInputError, "Category with class #{match[:class]} not recognized in string: #{string}"
|
|
141
|
+
end
|
|
187
142
|
end
|
|
188
|
-
end
|
|
189
143
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
144
|
+
def attribute(string)
|
|
145
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.attribute"
|
|
146
|
+
# create regular expression from regexp string
|
|
147
|
+
regexp = Regexp.new(REGEXP_ATTRIBUTE)
|
|
148
|
+
# match string to regular expression
|
|
149
|
+
match = regexp.match string
|
|
150
|
+
|
|
151
|
+
raise Occi::Errors::ParserInputError, "could not match #{string}" unless match
|
|
195
152
|
|
|
196
|
-
|
|
153
|
+
value = match[:string] if match[:string]
|
|
197
154
|
|
|
198
|
-
|
|
155
|
+
if match[:number]
|
|
156
|
+
match[:number].include?('.') ? value = match[:number].to_f : value = match[:number].to_i
|
|
157
|
+
end
|
|
199
158
|
|
|
200
|
-
|
|
201
|
-
|
|
159
|
+
value = match[:bool] == "true" if match[:bool]
|
|
160
|
+
Occi::Core::Attributes.split match[:name] => value
|
|
202
161
|
end
|
|
203
162
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
163
|
+
def link_string(string, source)
|
|
164
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.link_string"
|
|
165
|
+
# create regular expression from regexp string
|
|
166
|
+
regexp = Regexp.new( Occi::Settings.compatibility ? REGEXP_LINK : REGEXP_LINK_STRICT )
|
|
167
|
+
# match string to regular expression
|
|
168
|
+
match = regexp.match string
|
|
207
169
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
170
|
+
raise Occi::Errors::ParserInputError, "could not match #{string}" unless match
|
|
171
|
+
|
|
172
|
+
target = match[:uri]
|
|
173
|
+
rel = match[:rel]
|
|
174
|
+
if match[:category].blank?
|
|
175
|
+
kind = Occi::Core::Link.kind
|
|
176
|
+
else
|
|
177
|
+
categories = match[:category].split
|
|
178
|
+
kind = categories.shift
|
|
179
|
+
mixins = categories
|
|
180
|
+
end
|
|
181
|
+
actions = nil
|
|
182
|
+
location = match[:self]
|
|
183
|
+
|
|
184
|
+
# create an array of the list of attributes
|
|
185
|
+
attributes = []
|
|
186
|
+
regexp=Regexp.new '(\\s*'+REGEXP_ATTRIBUTE_REPR.to_s+')'
|
|
187
|
+
attr_line = match[:attributes].sub(/^\s*;\s*/, ' ')
|
|
188
|
+
attributes = attr_line.scan(regexp).collect {|matches| matches.first}
|
|
189
|
+
|
|
190
|
+
# parse each attribute and create an OCCI Attribute object from it
|
|
191
|
+
attributes = attributes.inject(Hashie::Mash.new) { |hsh, attribute|
|
|
192
|
+
hsh.merge!(Occi::Parser::Text.attribute("X-OCCI-Attribute: #{attribute}"))
|
|
193
|
+
}
|
|
194
|
+
Occi::Core::Link.new kind, mixins, attributes, actions, rel, target, source, location
|
|
224
195
|
end
|
|
225
|
-
actions = nil
|
|
226
|
-
location = match[:self]
|
|
227
|
-
|
|
228
|
-
# create an array of the list of attributes
|
|
229
|
-
attributes = []
|
|
230
|
-
regexp=Regexp.new '(\\s*'+REGEXP_ATTRIBUTE_REPR.to_s+')'
|
|
231
|
-
attr_line = match[:attributes].sub(/^\s*;\s*/, ' ')
|
|
232
|
-
attributes = attr_line.scan(regexp).collect {|matches| matches.first}
|
|
233
|
-
|
|
234
|
-
# parse each attribute and create an OCCI Attribute object from it
|
|
235
|
-
attributes = attributes.inject(Hashie::Mash.new) { |hsh, attribute| hsh.merge!(Occi::Parser::Text.attribute('X-OCCI-Attribute: ' + attribute)) }
|
|
236
|
-
Occi::Core::Link.new kind, mixins, attributes, actions, rel, target, source, location
|
|
237
|
-
end
|
|
238
196
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
197
|
+
def location(string)
|
|
198
|
+
Occi::Log.debug "[#{self}] Parsing through Occi::Parser::Text.location"
|
|
199
|
+
# create regular expression from regexp string
|
|
200
|
+
regexp = Regexp.new(REGEXP_LOCATION)
|
|
201
|
+
# match string to regular expression
|
|
202
|
+
match = regexp.match string
|
|
244
203
|
|
|
245
|
-
|
|
204
|
+
raise Occi::Errors::ParserInputError, "could not match #{string}" unless match
|
|
205
|
+
|
|
206
|
+
match[:location]
|
|
207
|
+
end
|
|
246
208
|
|
|
247
|
-
match[:location]
|
|
248
209
|
end
|
|
249
210
|
|
|
250
211
|
end
|
data/lib/occi/parser/xml.rb
CHANGED
|
@@ -4,14 +4,16 @@ module Occi
|
|
|
4
4
|
# @param [String] string
|
|
5
5
|
# @return [Occi::Collection]
|
|
6
6
|
def self.collection(string)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
parsed_xml = Nokogiri::XML(string) { |config| config.strict.nonet }
|
|
10
|
+
rescue Nokogiri::XML::SyntaxError => perr
|
|
11
|
+
Occi::Log.error "[#{self}] Failed to parse XML input: #{perr.message}"
|
|
12
|
+
raise Occi::Errors::ParserInputError, perr.message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
hash = Hashie::Mash.new(Hash.from_xml(parsed_xml))
|
|
16
|
+
Occi::Collection.new(hash)
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
end
|
data/lib/occi/parser.rb
CHANGED
|
@@ -1,51 +1,86 @@
|
|
|
1
|
-
|
|
2
|
-
require 'occi/parser/json'
|
|
3
|
-
require 'occi/parser/xml'
|
|
4
|
-
require 'occi/parser/ova'
|
|
5
|
-
require 'occi/parser/ovf'
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'parser', '*.rb')].each { |file| require file.gsub('.rb', '') }
|
|
6
2
|
|
|
7
3
|
module Occi
|
|
8
4
|
module Parser
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Occi::
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
6
|
+
class << self
|
|
7
|
+
|
|
8
|
+
OCCI_HEADERS = ['Category', 'Link', 'X-OCCI-Location', 'X-OCCI-Attribute', 'Location'].freeze
|
|
9
|
+
|
|
10
|
+
# Parses an OCCI message and extracts OCCI relevant information
|
|
11
|
+
# @param [String] media_type the media type of the OCCI message
|
|
12
|
+
# @param [String] body the body of the OCCI message
|
|
13
|
+
# @param [true, false] category for text/plain and text/occi media types information e.g. from the HTTP request location is needed to determine if the OCCI message includes a category or an entity
|
|
14
|
+
# @param [Occi::Core::Resource,Occi::Core::Link] entity_type entity type to use for parsing of text plain entities
|
|
15
|
+
# @param [Hash] header optional header of the OCCI message
|
|
16
|
+
# @return [Occi::Collection] list consisting of an array of locations and the OCCI object collection
|
|
17
|
+
def parse(media_type, body, category=false, entity_type=Occi::Core::Resource, header={})
|
|
18
|
+
Occi::Log.debug "[#{self}] Parsing request data to OCCI Collection"
|
|
19
|
+
header = headers_to_arys(header)
|
|
20
|
+
|
|
21
|
+
Occi::Log.debug "[#{self}] Parsing headers: #{header.inspect}"
|
|
22
|
+
collection = parse_headers(header, category, entity_type)
|
|
23
|
+
|
|
24
|
+
Occi::Log.debug "[#{self}] Parsing #{media_type} from body"
|
|
25
|
+
coll_body = parse_body(media_type, body, category, entity_type)
|
|
26
|
+
collection.merge! coll_body if coll_body && !coll_body.empty?
|
|
27
|
+
|
|
28
|
+
collection
|
|
32
29
|
end
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
def locations(media_type, body, header)
|
|
32
|
+
locations = []
|
|
33
|
+
locations << header['Location'] if header['Location'] && !header['Location'].blank?
|
|
34
|
+
header = headers_to_arys(header)
|
|
35
|
+
|
|
36
|
+
Occi::Log.debug "[#{self}] Parsing locations from request headers: #{header.inspect}"
|
|
37
|
+
locations << Occi::Parser::Text.locations(header)
|
|
38
|
+
|
|
39
|
+
Occi::Log.debug "[#{self}] Parsing #{media_type} locations from body"
|
|
40
|
+
case media_type
|
|
35
41
|
when 'text/uri-list'
|
|
42
|
+
locations << body.split("\n")
|
|
43
|
+
when 'text/plain', nil
|
|
44
|
+
locations << Occi::Parser::Text.locations(body.split "\n")
|
|
45
|
+
else
|
|
36
46
|
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
locations.flatten
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def parse_headers(header, category, entity_type)
|
|
55
|
+
if category
|
|
56
|
+
Occi::Log.debug "[#{self}] Parsing categories from headers"
|
|
57
|
+
collection = Occi::Parser::Text.categories(header)
|
|
58
|
+
else
|
|
59
|
+
if entity_type == Occi::Core::Resource
|
|
60
|
+
Occi::Log.debug "[#{self}] Parsing a resource from headers"
|
|
61
|
+
collection = Occi::Parser::Text.resource(header)
|
|
62
|
+
elsif entity_type == Occi::Core::Link
|
|
63
|
+
Occi::Log.debug "[#{self}] Parsing a link from headers"
|
|
64
|
+
collection = Occi::Parser::Text.link(header)
|
|
65
|
+
else
|
|
66
|
+
raise Occi::Errors::ParserTypeError, "Entity type '#{entity_type}' not supported"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
collection
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_body(media_type, body, category, entity_type)
|
|
74
|
+
collection = Occi::Collection.new
|
|
75
|
+
|
|
76
|
+
case media_type
|
|
77
|
+
when 'text/uri-list'
|
|
78
|
+
raise Occi::Errors::ParserTypeError, "Type 'text/uri-list' not supported by parse(). Call method #{self}.locations() to parse URI lists"
|
|
37
79
|
when 'text/occi'
|
|
80
|
+
Occi::Log.warn "Input type text/occi was passed to the parser in request body. All text/occi content MUST be passed in headers. Request body was not processed. The unprocessed content follows\n#{body}" unless body.blank?
|
|
38
81
|
nil
|
|
39
82
|
when 'text/plain', nil
|
|
40
|
-
|
|
41
|
-
collection = Occi::Parser::Text.categories body.split "\n"
|
|
42
|
-
else
|
|
43
|
-
if entity_type == Occi::Core::Resource
|
|
44
|
-
collection = Occi::Parser::Text.resource body.split "\n"
|
|
45
|
-
elsif entity_type == Occi::Core::Link
|
|
46
|
-
collection = Occi::Parser::Text.link body.split "\n"
|
|
47
|
-
end
|
|
48
|
-
end
|
|
83
|
+
collection = parse_body_plain(body, category, entity_type)
|
|
49
84
|
when 'application/occi+json', 'application/json'
|
|
50
85
|
collection = Occi::Parser::Json.collection body
|
|
51
86
|
when 'application/occi+xml', 'application/xml'
|
|
@@ -55,24 +90,39 @@ module Occi
|
|
|
55
90
|
when 'application/ova'
|
|
56
91
|
collection = Occi::Parser::Ova.collection body
|
|
57
92
|
else
|
|
58
|
-
raise "Content
|
|
93
|
+
raise Occi::Errors::ParserTypeError, "Content type #{media_type} not supported"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
collection
|
|
59
97
|
end
|
|
60
|
-
collection
|
|
61
|
-
end
|
|
62
98
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
case media_type
|
|
67
|
-
when 'text/uri-list'
|
|
68
|
-
locations << body.split("\n")
|
|
69
|
-
when 'text/plain', nil
|
|
70
|
-
locations << Occi::Parser::Text.locations(body.split "\n")
|
|
99
|
+
def parse_body_plain(body, category, entity_type)
|
|
100
|
+
if category
|
|
101
|
+
collection = Occi::Parser::Text.categories body.split "\n"
|
|
71
102
|
else
|
|
72
|
-
|
|
103
|
+
if entity_type == Occi::Core::Resource
|
|
104
|
+
collection = Occi::Parser::Text.resource body.split "\n"
|
|
105
|
+
elsif entity_type == Occi::Core::Link
|
|
106
|
+
collection = Occi::Parser::Text.link body.split "\n"
|
|
107
|
+
else
|
|
108
|
+
raise Occi::Errors::ParserTypeError, "Entity type #{entity_type} not supported"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
collection
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def headers_to_arys(header)
|
|
116
|
+
# remove the HTTP_ prefix if present and capitalize keys
|
|
117
|
+
header = Hash[header.map { |k, v| [k.gsub('HTTP_', '').capitalize, v] }]
|
|
118
|
+
header['X-OCCI-Location'] = header['X-occi-location'] if header['X-occi-location']
|
|
119
|
+
header['X-OCCI-Attribute'] = header['X-occi-attribute'] if header['X-occi-attribute']
|
|
120
|
+
|
|
121
|
+
header.delete_if { |k, v| v.blank? || !OCCI_HEADERS.include?(k) }
|
|
122
|
+
|
|
123
|
+
header.map { |k, v| v.to_s.split(',').collect { |w| "#{k}: #{w}" } }.flatten
|
|
73
124
|
end
|
|
74
125
|
|
|
75
|
-
locations.flatten
|
|
76
126
|
end
|
|
77
127
|
|
|
78
128
|
end
|
data/lib/occi/settings.rb
CHANGED
|
@@ -2,8 +2,9 @@ module Occi
|
|
|
2
2
|
class Settings < Settingslogic
|
|
3
3
|
gem_root = File.expand_path '../../..', __FILE__
|
|
4
4
|
|
|
5
|
+
source "#{ENV['HOME']}/.occi" if File.readable?("#{ENV['HOME']}/.occi")
|
|
5
6
|
source "#{gem_root}/config/occi.yml"
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
namespace 'core'
|
|
8
9
|
end
|
|
9
10
|
end
|
data/lib/occi/version.rb
CHANGED