occi-core 4.1.3 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +8 -0
  3. data/README.md +49 -17
  4. data/lib/occi/collection.rb +37 -21
  5. data/lib/occi/core/action.rb +5 -5
  6. data/lib/occi/core/action_instance.rb +45 -3
  7. data/lib/occi/core/actions.rb +2 -1
  8. data/lib/occi/core/attributes.rb +253 -73
  9. data/lib/occi/core/categories.rb +1 -0
  10. data/lib/occi/core/category.rb +25 -8
  11. data/lib/occi/core/entities.rb +1 -0
  12. data/lib/occi/core/entity.rb +51 -74
  13. data/lib/occi/core/kind.rb +15 -11
  14. data/lib/occi/core/kinds.rb +1 -1
  15. data/lib/occi/core/link.rb +14 -15
  16. data/lib/occi/core/links.rb +1 -1
  17. data/lib/occi/core/mixin.rb +5 -5
  18. data/lib/occi/core/mixins.rb +2 -2
  19. data/lib/occi/core/properties.rb +90 -12
  20. data/lib/occi/core/resource.rb +7 -3
  21. data/lib/occi/core/resources.rb +2 -2
  22. data/lib/occi/errors/attribute_definitions_converted_error.rb +5 -0
  23. data/lib/occi/errors/attribute_missing_error.rb +5 -0
  24. data/lib/occi/errors/attribute_name_invalid_error.rb +5 -0
  25. data/lib/occi/errors/attribute_not_defined_error.rb +5 -0
  26. data/lib/occi/errors/attribute_property_type_error.rb +5 -0
  27. data/lib/occi/errors/attribute_type_error.rb +5 -0
  28. data/lib/occi/errors/kind_not_defined_error.rb +5 -0
  29. data/lib/occi/errors/parser_input_error.rb +5 -0
  30. data/lib/occi/errors/parser_type_error.rb +5 -0
  31. data/lib/occi/errors.rb +1 -0
  32. data/lib/occi/extensions/hashie.rb +25 -0
  33. data/lib/occi/helpers/comparators/action_instance.rb +22 -0
  34. data/lib/occi/helpers/comparators/attributes.rb +22 -0
  35. data/lib/occi/helpers/comparators/categories.rb +22 -0
  36. data/lib/occi/helpers/comparators/category.rb +22 -0
  37. data/lib/occi/helpers/comparators/collection.rb +40 -0
  38. data/lib/occi/helpers/comparators/entities.rb +22 -0
  39. data/lib/occi/helpers/comparators/entity.rb +22 -0
  40. data/lib/occi/helpers/comparators/properties.rb +26 -0
  41. data/lib/occi/helpers/comparators.rb +1 -0
  42. data/lib/occi/infrastructure/compute.rb +11 -9
  43. data/lib/occi/infrastructure/network.rb +27 -27
  44. data/lib/occi/infrastructure/networkinterface.rb +22 -23
  45. data/lib/occi/infrastructure/os_tpl.rb +1 -1
  46. data/lib/occi/infrastructure/resource_tpl.rb +1 -1
  47. data/lib/occi/infrastructure/storage.rb +7 -6
  48. data/lib/occi/infrastructure/storagelink.rb +4 -4
  49. data/lib/occi/log.rb +13 -10
  50. data/lib/occi/model.rb +9 -8
  51. data/lib/occi/parser/json.rb +11 -9
  52. data/lib/occi/parser/ova.rb +12 -6
  53. data/lib/occi/parser/ovf.rb +173 -116
  54. data/lib/occi/parser/text/constants.rb +87 -0
  55. data/lib/occi/parser/text.rb +161 -200
  56. data/lib/occi/parser/xml.rb +10 -8
  57. data/lib/occi/parser.rb +100 -50
  58. data/lib/occi/settings.rb +2 -1
  59. data/lib/occi/version.rb +1 -1
  60. data/lib/occi-core.rb +6 -4
  61. data/occi-core.gemspec +0 -7
  62. data/spec/occi/collection_samples/collection1.json +1 -0
  63. data/spec/occi/collection_samples/directory2/collection2.json +1 -0
  64. data/spec/occi/collection_spec.rb +961 -31
  65. data/spec/occi/core/action_instance_spec.rb +317 -0
  66. data/spec/occi/core/action_spec.rb +71 -0
  67. data/spec/occi/core/attributes_spec.rb +582 -27
  68. data/spec/occi/core/category_spec.rb +194 -18
  69. data/spec/occi/core/entities_spec.rb +96 -0
  70. data/spec/occi/core/entity_spec.rb +317 -28
  71. data/spec/occi/core/kind_spec.rb +127 -16
  72. data/spec/occi/core/link_spec.rb +35 -0
  73. data/spec/occi/core/links_spec.rb +130 -0
  74. data/spec/occi/core/mixins_spec.rb +107 -0
  75. data/spec/occi/core/properties_spec.rb +167 -0
  76. data/spec/occi/core/resource_spec.rb +23 -9
  77. data/spec/occi/core_spec.rb +12 -0
  78. data/spec/occi/infrastructure/compute_spec.rb +218 -18
  79. data/spec/occi/infrastructure/network_spec.rb +96 -0
  80. data/spec/occi/infrastructure/networkinterface_spec.rb +96 -0
  81. data/spec/occi/infrastructure/storage_spec.rb +33 -0
  82. data/spec/occi/infrastructure/storagelink_spec.rb +45 -0
  83. data/spec/occi/log_spec.rb +104 -1
  84. data/spec/occi/model_spec.rb +251 -39
  85. data/spec/occi/{test.json → parser/json_samples/test.json} +0 -0
  86. data/spec/occi/parser/ova_samples/test.dump +0 -0
  87. data/spec/occi/{test.ova → parser/ova_samples/test.ova} +0 -0
  88. data/spec/occi/parser/ovf_samples/test.dump +0 -0
  89. data/spec/occi/{test.ovf → parser/ovf_samples/test.ovf} +0 -0
  90. data/spec/occi/parser/text_samples/occi_categories.dump +0 -0
  91. data/spec/occi/parser/text_samples/occi_categories.text +2 -0
  92. data/spec/occi/parser/text_samples/occi_compute_rocci_server.dump +0 -0
  93. data/spec/occi/parser/text_samples/occi_compute_rocci_server.resource.dump +0 -0
  94. data/spec/occi/parser/text_samples/occi_compute_rocci_server.text +10 -0
  95. data/spec/occi/parser/text_samples/occi_link_resource_instance.dump +0 -0
  96. data/spec/occi/parser/text_samples/occi_link_resource_instance.text +7 -0
  97. data/spec/occi/parser/text_samples/occi_link_simple.dump +0 -0
  98. data/spec/occi/parser/text_samples/occi_link_simple.link_string.dump +0 -0
  99. data/spec/occi/parser/text_samples/occi_link_simple.text +1 -0
  100. data/spec/occi/parser/text_samples/occi_link_w_attributes.dump +0 -0
  101. data/spec/occi/parser/text_samples/occi_link_w_attributes.text +7 -0
  102. data/spec/occi/parser/text_samples/occi_link_w_category.dump +0 -0
  103. data/spec/occi/parser/text_samples/occi_link_w_category.text +3 -0
  104. data/spec/occi/parser/text_samples/occi_model_rocci_server.dump +0 -0
  105. data/spec/occi/parser/text_samples/occi_model_rocci_server.text +51 -0
  106. data/spec/occi/parser/text_samples/occi_network_rocci_server.dump +0 -0
  107. data/spec/occi/parser/text_samples/occi_network_rocci_server.resource.dump +0 -0
  108. data/spec/occi/parser/text_samples/occi_network_rocci_server.text +11 -0
  109. data/spec/occi/parser/text_samples/occi_resource_w_attributes.dump +0 -0
  110. data/spec/occi/parser/text_samples/occi_resource_w_attributes.text +11 -0
  111. data/spec/occi/parser/text_samples/occi_resource_w_inline_links.dump +0 -0
  112. data/spec/occi/parser/text_samples/occi_resource_w_inline_links.text +16 -0
  113. data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.dump +0 -0
  114. data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.text +13 -0
  115. data/spec/occi/parser/text_samples/occi_storage_rocci_server.dump +0 -0
  116. data/spec/occi/parser/text_samples/occi_storage_rocci_server.resource.dump +0 -0
  117. data/spec/occi/parser/text_samples/occi_storage_rocci_server.text +9 -0
  118. data/spec/occi/parser/text_spec.rb +274 -78
  119. data/spec/occi/parser/xml_samples/test.xml +352 -0
  120. data/spec/occi/parser_spec.rb +255 -104
  121. data/spec/occi-core_spec.rb +31 -0
  122. data/spec/spec_helper.rb +6 -2
  123. metadata +110 -111
  124. checksums.yaml +0 -7
  125. data/spec/occi/core/attribute_spec.rb +0 -0
@@ -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
- # Regular expressions
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
- # Regular expression for OCCI Entity Attributes
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
- # Regular expression for OCCI Location
84
- REGEXP_LOCATION = "X-OCCI-Location:\\s*(?<location>#{URI::URI_REF})" <<
85
- ';?' # additional semicolon at the end (not specified, for interoperability)
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
- def self.categories(lines)
89
- collection = Occi::Collection.new
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
- def self.resource(lines)
99
- collection = Occi::Collection.new
100
- resource = Occi::Core::Resource.new
101
- lines.each do |line|
102
- line.strip!
103
- case line
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 = self.category(line)
106
- resource.kind = category if category.kind_of? Occi::Core::Kind
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! self.attribute(line)
44
+ resource.attributes.merge! attribute(line)
110
45
  when /^Link:/
111
- link = self.link_string(line, resource)
46
+ link = link_string(line, resource)
112
47
  resource.links << link
113
- collection << link
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
- def self.link(lines)
121
- collection = Occi::Collection.new
122
- link = Occi::Core::Link.new
123
- lines.each do |line|
124
- line.strip!
125
- case line
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 = self.category(line)
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! self.attribute(line)
132
- end
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
- def self.locations(lines)
139
- locations = []
140
- lines.each do |line|
141
- line.strip!
142
- locations << self.location(line) if line.start_with? 'X-OCCI-Location:'
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
- private
148
-
149
- def self.category(string)
150
- # create regular expression from regexp string
151
- regexp = Regexp.new(REGEXP_CATEGORY)
152
- # match string to regular expression
153
- match = regexp.match string
154
-
155
- raise "could not match #{string}" unless match
156
-
157
- term = match[:term]
158
- scheme = match[:scheme]
159
- title = match[:title]
160
- related = match[:rel].to_s.split
161
- attributes = Occi::Core::Attributes.new
162
- if match[:attributes]
163
- matched_attributes = match[:attributes].gsub(/\{(immutable|required)\s+(required|immutable)\}/, '{\1_\2}')
164
- matched_attributes.split.each do |attribute|
165
- attribute.gsub! /\{(immutable|required)_(required|immutable)\}/, '{\1 \2}'
166
- property_string = attribute[/#{REGEXP_ATTRIBUTE_DEF}/, -2]
167
- properties = Occi::Core::Properties.new
168
- if property_string
169
- properties.required = true if property_string.include? 'required'
170
- properties.mutable = false if property_string.include? 'immutable'
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
- end
176
- actions = match[:actions].to_s.split
177
- location = match[:location]
178
- case match[:class]
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
- def self.attribute(string)
191
- # create regular expression from regexp string
192
- regexp = Regexp.new(REGEXP_ATTRIBUTE)
193
- # match string to regular expression
194
- match = regexp.match string
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
- raise "could not match #{string}" unless match
153
+ value = match[:string] if match[:string]
197
154
 
198
- value = match[:string] if match[:string]
155
+ if match[:number]
156
+ match[:number].include?('.') ? value = match[:number].to_f : value = match[:number].to_i
157
+ end
199
158
 
200
- if match[:number]
201
- match[:number].include?('.') ? value = match[:number].to_f : value = match[:number].to_i
159
+ value = match[:bool] == "true" if match[:bool]
160
+ Occi::Core::Attributes.split match[:name] => value
202
161
  end
203
162
 
204
- value = match[:bool] == "true" if match[:bool]
205
- Occi::Core::Attributes.split match[:name] => value
206
- end
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
- def self.link_string(string, source)
209
- # create regular expression from regexp string
210
- regexp = Regexp.new(REGEXP_LINK)
211
- # match string to regular expression
212
- match = regexp.match string
213
-
214
- raise "could not match #{string}" unless match
215
-
216
- target = match[:uri]
217
- rel = match[:rel]
218
- if match[:category].blank?
219
- kind = Occi::Core::Link.kind
220
- else
221
- categories = match[:category].split
222
- kind = categories.shift
223
- mixins = categories
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
- def self.location(string)
240
- # create regular expression from regexp string
241
- regexp = Regexp.new(REGEXP_LOCATION)
242
- # match string to regular expression
243
- match = regexp.match string
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
- raise "could not match #{string}" unless match
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
@@ -4,14 +4,16 @@ module Occi
4
4
  # @param [String] string
5
5
  # @return [Occi::Collection]
6
6
  def self.collection(string)
7
- collection = Occi::Collection.new
8
- hash = Hashie::Mash.new(Hash.from_xml(Nokogiri::XML(string)))
9
- collection.kinds.merge hash.kinds.collect { |kind| Occi::Core::Kind.new(kind.scheme, kind.term, kind.title, kind.attributes, kind.related, kind.actions) } if hash.kinds
10
- collection.mixins.merge hash.mixins.collect { |mixin| Occi::Core::Mixin.new(mixin.scheme, mixin.term, mixin.title, mixin.attributes, mixin.related, mixin.actions) } if hash.mixins
11
- collection.actions.merge hash.actions.collect { |action| Occi::Core::Action.new(action.scheme, action.term, action.title, action.attributes) } if hash.actions
12
- collection.resources.merge hash.resources.collect { |resource| Occi::Core::Resource.new(resource.kind, resource.mixins, resource.attributes, resource.actions, resource.links) } if hash.resources
13
- collection.links.merge hash.links.collect { |link| Occi::Core::Link.new(link.kind, link.mixins, link.attributes) } if hash.links
14
- collection
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
- require 'occi/parser/text'
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
- # 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 self.parse(media_type, body, category=false, entity_type=Occi::Core::Resource, header={})
18
- Occi::Log.debug '### Parsing request data to OCCI Collection ###'
19
- collection = Occi::Collection.new
20
-
21
- # remove trailing HTTP_ prefix if present
22
- header = Hash[header.map { |k, v| [k.gsub('HTTP_', '').upcase, v] }]
23
-
24
- if category
25
- collection = Occi::Parser::Text.categories(header.map { |k, v| v.to_s.split(',').collect { |w| "#{k}: #{w}" } }.flatten)
26
- else
27
- if entity_type == Occi::Core::Resource
28
- collection = Occi::Parser::Text.resource(header.map { |k, v| v.to_s.split(',').collect { |w| "#{k}: #{w}" } }.flatten)
29
- elsif entity_type == Occi::Core::Link
30
- collection = Occi::Parser::Text.link(header.map { |k, v| v.to_s.split(',').collect { |w| "#{k}: #{w}" } }.flatten)
31
- end
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
- case media_type
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
- if category
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 Type not supported"
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
- def self.locations(media_type, body, header)
64
- locations = Occi::Parser::Text.locations header.map { |k, v| v.to_s.split(',').collect { |w| "#{k}: #{w}" } }.flatten
65
- locations << header['Location'] if !header['Location'].nil? && header['Location'].any?
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
- nil
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
- source open(ENV['HOME']+'/.occi') if File.file?(ENV['HOME']+'/.occi')
7
+
7
8
  namespace 'core'
8
9
  end
9
10
  end
data/lib/occi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Occi
2
- VERSION = "4.1.3" unless defined?(::Occi::VERSION)
2
+ VERSION = "4.2.0" unless defined?(::Occi::VERSION)
3
3
  end