2
0

resources.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. """
  2. Base implementation for data objects exposed through a provider or service
  3. """
  4. import inspect
  5. import itertools
  6. import logging
  7. import os
  8. import re
  9. import shutil
  10. import time
  11. import cloudbridge.cloud.base.helpers as cb_helpers
  12. from cloudbridge.cloud.interfaces.exceptions \
  13. import InvalidConfigurationException
  14. from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
  15. from cloudbridge.cloud.interfaces.exceptions import WaitStateException
  16. from cloudbridge.cloud.interfaces.resources import AttachmentInfo
  17. from cloudbridge.cloud.interfaces.resources import Bucket
  18. from cloudbridge.cloud.interfaces.resources import BucketContainer
  19. from cloudbridge.cloud.interfaces.resources import BucketObject
  20. from cloudbridge.cloud.interfaces.resources import CloudResource
  21. from cloudbridge.cloud.interfaces.resources import FloatingIP
  22. from cloudbridge.cloud.interfaces.resources import FloatingIPContainer
  23. from cloudbridge.cloud.interfaces.resources import FloatingIpState
  24. from cloudbridge.cloud.interfaces.resources import GatewayState
  25. from cloudbridge.cloud.interfaces.resources import Instance
  26. from cloudbridge.cloud.interfaces.resources import InstanceState
  27. from cloudbridge.cloud.interfaces.resources import InternetGateway
  28. from cloudbridge.cloud.interfaces.resources import KeyPair
  29. from cloudbridge.cloud.interfaces.resources import LaunchConfig
  30. from cloudbridge.cloud.interfaces.resources import MachineImage
  31. from cloudbridge.cloud.interfaces.resources import MachineImageState
  32. from cloudbridge.cloud.interfaces.resources import Network
  33. from cloudbridge.cloud.interfaces.resources import NetworkState
  34. from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
  35. from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
  36. from cloudbridge.cloud.interfaces.resources import PlacementZone
  37. from cloudbridge.cloud.interfaces.resources import Region
  38. from cloudbridge.cloud.interfaces.resources import ResultList
  39. from cloudbridge.cloud.interfaces.resources import Router
  40. from cloudbridge.cloud.interfaces.resources import Snapshot
  41. from cloudbridge.cloud.interfaces.resources import SnapshotState
  42. from cloudbridge.cloud.interfaces.resources import Subnet
  43. from cloudbridge.cloud.interfaces.resources import SubnetState
  44. from cloudbridge.cloud.interfaces.resources import VMFirewall
  45. from cloudbridge.cloud.interfaces.resources import VMFirewallRule
  46. from cloudbridge.cloud.interfaces.resources import VMFirewallRuleContainer
  47. from cloudbridge.cloud.interfaces.resources import VMType
  48. from cloudbridge.cloud.interfaces.resources import Volume
  49. from cloudbridge.cloud.interfaces.resources import VolumeState
  50. import six
  51. log = logging.getLogger(__name__)
  52. class BaseCloudResource(CloudResource):
  53. """
  54. Base implementation of a CloudBridge Resource.
  55. """
  56. # Regular expression for valid cloudbridge resource names.
  57. # They, must match the same criteria as GCE labels.
  58. # as discussed here: https://github.com/gvlproject/cloudbridge/issues/55
  59. #
  60. # NOTE: The following regex is based on GCEs internal validation logic,
  61. # and is significantly complex to allow for international characters.
  62. CB_NAME_PATTERN = re.compile(six.u(
  63. r"^[\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105"
  64. "\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B"
  65. "\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131"
  66. "\u0133\u0135\u0137\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146"
  67. "\u0148\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B"
  68. "\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171"
  69. "\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C"
  70. "\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA"
  71. "\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9\u01BA\u01BD-\u01BF\u01C6\u01C9"
  72. "\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC\u01DD\u01DF"
  73. "\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF\u01F0\u01F3\u01F5"
  74. "\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D"
  75. "\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223"
  76. "\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F"
  77. "\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371"
  78. "\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0\u03D1\u03D5-"
  79. "\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB"
  80. "\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB\u03FC\u0430-\u045F\u0461\u0463"
  81. "\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479"
  82. "\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497"
  83. "\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD"
  84. "\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4"
  85. "\u04C6\u04C8\u04CA\u04CC\u04CE\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9"
  86. "\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF"
  87. "\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505"
  88. "\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B"
  89. "\u051D\u051F\u0521\u0523\u0525\u0527\u0561-\u0587\u1D00-\u1D2B"
  90. "\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D"
  91. "\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23"
  92. "\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39"
  93. "\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F"
  94. "\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65"
  95. "\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B"
  96. "\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91"
  97. "\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD"
  98. "\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3"
  99. "\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9"
  100. "\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF"
  101. "\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15"
  102. "\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67"
  103. "\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4"
  104. "\u1FB6\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6\u1FC7\u1FD0-\u1FD3\u1FD6\u1FD7"
  105. "\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6\u1FF7\u210A\u210E\u210F\u2113\u212F"
  106. "\u2134\u2139\u213C\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61"
  107. "\u2C65\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73\u2C74\u2C76-\u2C7B\u2C81"
  108. "\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97"
  109. "\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD"
  110. "\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3"
  111. "\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9"
  112. "\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25"
  113. "\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651"
  114. "\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667"
  115. "\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F"
  116. "\uA691\uA693\uA695\uA697\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-"
  117. "\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745"
  118. "\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B"
  119. "\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-"
  120. "\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791"
  121. "\uA793\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7FA\uFB00-\uFB06\uFB13-\uFB17"
  122. "\uFF41-\uFF5A\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA"
  123. "\u05F0-\u05F2\u0620-\u063F\u0641-\u064A\u066E\u066F\u0671-\u06D3"
  124. "\u06D5\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-"
  125. "\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u08A0\u08A2-"
  126. "\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0977\u0979-"
  127. "\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2"
  128. "\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1"
  129. "\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33"
  130. "\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-"
  131. "\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-"
  132. "\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-"
  133. "\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D"
  134. "\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95"
  135. "\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-"
  136. "\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33"
  137. "\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-"
  138. "\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0"
  139. "\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D"
  140. "\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-"
  141. "\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E45"
  142. "\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-"
  143. "\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2"
  144. "\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-"
  145. "\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D"
  146. "\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10D0-\u10FA"
  147. "\u10FD-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-"
  148. "\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0"
  149. "\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A"
  150. "\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A"
  151. "\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751"
  152. "\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-"
  153. "\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D"
  154. "\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54"
  155. "\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5"
  156. "\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1"
  157. "\u1CF5\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6"
  158. "\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE"
  159. "\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-"
  160. "\u30FA\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF"
  161. "\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7"
  162. "\uA500-\uA60B\uA610-\uA61F\uA62A\uA62B\uA66E\uA6A0-\uA6E5\uA7FB-"
  163. "\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-"
  164. "\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C"
  165. "\uA984-\uA9B2\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F"
  166. "\uAA71-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD"
  167. "\uAAC0\uAAC2\uAADB\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-"
  168. "\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-"
  169. "\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D"
  170. "\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43"
  171. "\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-"
  172. "\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-"
  173. "\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u0030-"
  174. "\u0039\u00B2\u00B3\u00B9\u00BC-\u00BE\u0660-\u0669\u06F0-\u06F9"
  175. "\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F"
  176. "\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F"
  177. "\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0E50-\u0E59\u0ED0-\u0ED9"
  178. "\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0"
  179. "\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA"
  180. "\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49"
  181. "\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-"
  182. "\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-"
  183. "\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-"
  184. "\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-"
  185. "\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-"
  186. "\uABF9\uFF10-\uFF19_-]{0,63}$"), re.UNICODE)
  187. def __init__(self, provider):
  188. self.__provider = provider
  189. @staticmethod
  190. def is_valid_resource_name(name):
  191. return True if BaseCloudResource.CB_NAME_PATTERN.match(name) else False
  192. @staticmethod
  193. def assert_valid_resource_name(name):
  194. if not BaseCloudResource.is_valid_resource_name(name):
  195. log.debug("InvalidNameException raised on %s", name, exc_info=True)
  196. raise InvalidNameException(
  197. u"Invalid name: %s. Name must be at most 63 characters "
  198. "long and consist of lowercase letters, numbers, "
  199. "underscores, dashes or international characters" % name)
  200. @property
  201. def _provider(self):
  202. return self.__provider
  203. def to_json(self):
  204. # Get all attributes but filter methods and private/magic ones
  205. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  206. js = {k: v for(k, v) in attr if not k.startswith('_')}
  207. return js
  208. class BaseObjectLifeCycleMixin(ObjectLifeCycleMixin):
  209. """
  210. A base implementation of an ObjectLifeCycleMixin.
  211. This base implementation has an implementation of wait_for
  212. which refreshes the object's state till the desired ready states
  213. are reached. Subclasses must still implement the wait_till_ready
  214. method, since the desired ready states are object specific.
  215. """
  216. def wait_for(self, target_states, terminal_states=None, timeout=None,
  217. interval=None):
  218. if timeout is None:
  219. timeout = self._provider.config.default_wait_timeout
  220. if interval is None:
  221. interval = self._provider.config.default_wait_interval
  222. assert timeout >= 0
  223. assert interval >= 0
  224. assert timeout >= interval
  225. end_time = time.time() + timeout
  226. while self.state not in target_states:
  227. if self.state in (terminal_states or []):
  228. raise WaitStateException(
  229. "Object: {0} is in state: {1} which is a terminal state"
  230. " and cannot be waited on.".format(self, self.state))
  231. else:
  232. log.debug(
  233. "Object %s is in state: %s. Waiting another %s"
  234. " seconds to reach target state(s): %s...",
  235. self,
  236. self.state,
  237. int(end_time - time.time()),
  238. target_states)
  239. time.sleep(interval)
  240. if time.time() > end_time:
  241. raise WaitStateException(
  242. "Waited too long for object: {0} to become ready. It's"
  243. " still in state: {1}".format(self, self.state))
  244. self.refresh()
  245. log.debug("Object: %s successfully reached target state: %s",
  246. self, self.state)
  247. return True
  248. class BaseResultList(ResultList):
  249. def __init__(
  250. self, is_truncated, marker, supports_total, total=None, data=None):
  251. # call list constructor
  252. super(BaseResultList, self).__init__(data or [])
  253. self._marker = marker
  254. self._is_truncated = is_truncated
  255. self._supports_total = True if supports_total else False
  256. self._total = total
  257. @property
  258. def marker(self):
  259. return self._marker
  260. @property
  261. def is_truncated(self):
  262. return self._is_truncated
  263. @property
  264. def supports_total(self):
  265. return self._supports_total
  266. @property
  267. def total_results(self):
  268. return self._total
  269. class ServerPagedResultList(BaseResultList):
  270. """
  271. This is a convenience class that extends the :class:`BaseResultList` class
  272. and provides a server side implementation of paging. It is meant for use by
  273. provider developers and is not meant for direct use by end-users.
  274. This class can be used to wrap a partial result list when an operation
  275. supports server side paging.
  276. """
  277. @property
  278. def supports_server_paging(self):
  279. return True
  280. @property
  281. def data(self):
  282. raise NotImplementedError(
  283. "ServerPagedResultLists do not support the data property")
  284. class ClientPagedResultList(BaseResultList):
  285. """
  286. This is a convenience class that extends the :class:`BaseResultList` class
  287. and provides a client side implementation of paging. It is meant for use by
  288. provider developers and is not meant for direct use by end-users.
  289. This class can be used to wrap a full result list when an operation does
  290. not support server side paging. This class will then provide a paged view
  291. of the full result set entirely on the client side.
  292. """
  293. def __init__(self, provider, objects, limit=None, marker=None):
  294. self._objects = objects
  295. limit = limit or provider.config.default_result_limit
  296. total_size = len(objects)
  297. if marker:
  298. from_marker = itertools.dropwhile(
  299. lambda obj: not obj.id == marker, objects)
  300. # skip one past the marker
  301. next(from_marker, None)
  302. objects = list(from_marker)
  303. is_truncated = len(objects) > limit
  304. results = list(itertools.islice(objects, limit))
  305. super(ClientPagedResultList, self).__init__(
  306. is_truncated,
  307. results[-1].id if is_truncated else None,
  308. True, total=total_size,
  309. data=results)
  310. @property
  311. def supports_server_paging(self):
  312. return False
  313. @property
  314. def data(self):
  315. return self._objects
  316. class BasePageableObjectMixin(PageableObjectMixin):
  317. """
  318. A mixin to provide iteration capability for a class
  319. that support a list(limit, marker) method.
  320. """
  321. def __iter__(self):
  322. result_list = self.list()
  323. if result_list.supports_server_paging:
  324. for result in result_list:
  325. yield result
  326. while result_list.is_truncated:
  327. result_list = self.list(marker=result_list.marker)
  328. for result in result_list:
  329. yield result
  330. else:
  331. for result in result_list.data:
  332. yield result
  333. class BaseVMType(BaseCloudResource, VMType):
  334. def __init__(self, provider):
  335. super(BaseVMType, self).__init__(provider)
  336. def __eq__(self, other):
  337. return (isinstance(other, VMType) and
  338. # pylint:disable=protected-access
  339. self._provider == other._provider and
  340. self.id == other.id)
  341. @property
  342. def size_total_disk(self):
  343. return self.size_root_disk + self.size_ephemeral_disks
  344. def __repr__(self):
  345. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  346. self.name, self.id)
  347. class BaseInstance(BaseCloudResource, BaseObjectLifeCycleMixin, Instance):
  348. def __init__(self, provider):
  349. super(BaseInstance, self).__init__(provider)
  350. def __eq__(self, other):
  351. return (isinstance(other, Instance) and
  352. # pylint:disable=protected-access
  353. self._provider == other._provider and
  354. self.id == other.id and
  355. # check from most to least likely mutables
  356. self.state == other.state and
  357. self.name == other.name and
  358. self.vm_firewalls == other.vm_firewalls and
  359. self.public_ips == other.public_ips and
  360. self.private_ips == other.private_ips and
  361. self.image_id == other.image_id)
  362. def wait_till_ready(self, timeout=None, interval=None):
  363. self.wait_for(
  364. [InstanceState.RUNNING],
  365. terminal_states=[InstanceState.DELETED, InstanceState.ERROR],
  366. timeout=timeout,
  367. interval=interval)
  368. def __repr__(self):
  369. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  370. self.name, self.id)
  371. class BaseLaunchConfig(LaunchConfig):
  372. def __init__(self, provider):
  373. self.provider = provider
  374. self.block_devices = []
  375. class BlockDeviceMapping(object):
  376. """
  377. Represents a block device mapping
  378. """
  379. def __init__(self, is_volume=False, source=None, is_root=None,
  380. size=None, delete_on_terminate=None):
  381. self.is_volume = is_volume
  382. self.source = source
  383. self.is_root = is_root
  384. self.size = size
  385. self.delete_on_terminate = delete_on_terminate
  386. def add_ephemeral_device(self):
  387. block_device = BaseLaunchConfig.BlockDeviceMapping()
  388. self.block_devices.append(block_device)
  389. def add_volume_device(self, source=None, is_root=None, size=None,
  390. delete_on_terminate=None):
  391. block_device = self._validate_volume_device(
  392. source=source, is_root=is_root, size=size,
  393. delete_on_terminate=delete_on_terminate)
  394. log.debug("Appending %s to the block_devices list",
  395. block_device)
  396. self.block_devices.append(block_device)
  397. def _validate_volume_device(self, source=None, is_root=None,
  398. size=None, delete_on_terminate=None):
  399. """
  400. Validates a volume based device and throws an
  401. InvalidConfigurationException if the configuration is incorrect.
  402. """
  403. if source is None and not size:
  404. log.exception("InvalidConfigurationException raised: "
  405. "no size argument specified.")
  406. raise InvalidConfigurationException(
  407. "A size must be specified for a blank new volume.")
  408. if source and \
  409. not isinstance(source, (Snapshot, Volume, MachineImage)):
  410. log.exception("InvalidConfigurationException raised: "
  411. "source argument not specified correctly.")
  412. raise InvalidConfigurationException(
  413. "Source must be a Snapshot, Volume, MachineImage, or None.")
  414. if size:
  415. if not isinstance(size, six.integer_types) or not size > 0:
  416. log.exception("InvalidConfigurationException raised: "
  417. "size argument must be an integer greater than "
  418. "0. Got type %s and value %s.", type(size), size)
  419. raise InvalidConfigurationException(
  420. "The size must be None or an integer greater than 0.")
  421. if is_root:
  422. for bd in self.block_devices:
  423. if bd.is_root:
  424. log.exception("InvalidConfigurationException raised: "
  425. "%s has already been marked as the root "
  426. "block device.", bd)
  427. raise InvalidConfigurationException(
  428. "An existing block device: {0} has already been"
  429. " marked as root. There can only be one root device.")
  430. return BaseLaunchConfig.BlockDeviceMapping(
  431. is_volume=True, source=source, is_root=is_root, size=size,
  432. delete_on_terminate=delete_on_terminate)
  433. class BaseMachineImage(
  434. BaseCloudResource, BaseObjectLifeCycleMixin, MachineImage):
  435. def __init__(self, provider):
  436. super(BaseMachineImage, self).__init__(provider)
  437. def __eq__(self, other):
  438. return (isinstance(other, MachineImage) and
  439. # pylint:disable=protected-access
  440. self._provider == other._provider and
  441. self.id == other.id and
  442. # check from most to least likely mutables
  443. self.state == other.state and
  444. self.name == other.name and
  445. self.description == other.description)
  446. def wait_till_ready(self, timeout=None, interval=None):
  447. self.wait_for(
  448. [MachineImageState.AVAILABLE],
  449. terminal_states=[MachineImageState.ERROR],
  450. timeout=timeout,
  451. interval=interval)
  452. def __repr__(self):
  453. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  454. self.name, self.id)
  455. class BaseAttachmentInfo(AttachmentInfo):
  456. def __init__(self, volume, instance_id, device):
  457. self._volume = volume
  458. self._instance_id = instance_id
  459. self._device = device
  460. @property
  461. def volume(self):
  462. return self._volume
  463. @property
  464. def instance_id(self):
  465. return self._instance_id
  466. @property
  467. def device(self):
  468. return self._device
  469. class BaseVolume(BaseCloudResource, BaseObjectLifeCycleMixin, Volume):
  470. def __init__(self, provider):
  471. super(BaseVolume, self).__init__(provider)
  472. def __eq__(self, other):
  473. return (isinstance(other, Volume) and
  474. # pylint:disable=protected-access
  475. self._provider == other._provider and
  476. self.id == other.id and
  477. # check from most to least likely mutables
  478. self.state == other.state and
  479. self.name == other.name)
  480. def wait_till_ready(self, timeout=None, interval=None):
  481. self.wait_for(
  482. [VolumeState.AVAILABLE],
  483. terminal_states=[VolumeState.ERROR, VolumeState.DELETED],
  484. timeout=timeout,
  485. interval=interval)
  486. def __repr__(self):
  487. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  488. self.name, self.id)
  489. class BaseSnapshot(BaseCloudResource, BaseObjectLifeCycleMixin, Snapshot):
  490. def __init__(self, provider):
  491. super(BaseSnapshot, self).__init__(provider)
  492. def __eq__(self, other):
  493. return (isinstance(other, Snapshot) and
  494. # pylint:disable=protected-access
  495. self._provider == other._provider and
  496. self.id == other.id and
  497. # check from most to least likely mutables
  498. self.state == other.state and
  499. self.name == other.name)
  500. def wait_till_ready(self, timeout=None, interval=None):
  501. self.wait_for(
  502. [SnapshotState.AVAILABLE],
  503. terminal_states=[SnapshotState.ERROR],
  504. timeout=timeout,
  505. interval=interval)
  506. def __repr__(self):
  507. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  508. self.name, self.id)
  509. class BaseKeyPair(BaseCloudResource, KeyPair):
  510. def __init__(self, provider, key_pair):
  511. super(BaseKeyPair, self).__init__(provider)
  512. self._key_pair = key_pair
  513. self._private_material = None
  514. def __eq__(self, other):
  515. return (isinstance(other, KeyPair) and
  516. # pylint:disable=protected-access
  517. self._provider == other._provider and
  518. self.name == other.name)
  519. @property
  520. def id(self):
  521. """
  522. Return the id of this key pair.
  523. """
  524. return self._key_pair.name
  525. @property
  526. def name(self):
  527. """
  528. Return the name of this key pair.
  529. """
  530. return self._key_pair.name
  531. @property
  532. def material(self):
  533. return self._private_material
  534. @material.setter
  535. # pylint:disable=arguments-differ
  536. def material(self, value):
  537. self._private_material = value
  538. def delete(self):
  539. """
  540. Delete this KeyPair.
  541. :rtype: bool
  542. :return: True if successful, otherwise False.
  543. """
  544. # This implementation assumes the `delete` method exists across
  545. # multiple providers.
  546. self._key_pair.delete()
  547. def __repr__(self):
  548. return "<CBKeyPair: {0}>".format(self.name)
  549. class BaseVMFirewall(BaseCloudResource, VMFirewall):
  550. def __init__(self, provider, vm_firewall):
  551. super(BaseVMFirewall, self).__init__(provider)
  552. self._vm_firewall = vm_firewall
  553. def __eq__(self, other):
  554. """
  555. Check if all the defined rules match across both VM firewalls.
  556. """
  557. return (isinstance(other, VMFirewall) and
  558. # pylint:disable=protected-access
  559. self._provider == other._provider and
  560. set(self.rules) == set(other.rules))
  561. def __ne__(self, other):
  562. return not self.__eq__(other)
  563. @property
  564. def id(self):
  565. """
  566. Get the ID of this VM firewall.
  567. :rtype: str
  568. :return: VM firewall ID
  569. """
  570. return self._vm_firewall.id
  571. @property
  572. def name(self):
  573. """
  574. Return the name of this VM firewall.
  575. """
  576. return self._vm_firewall.name
  577. @property
  578. def description(self):
  579. """
  580. Return the description of this VM firewall.
  581. """
  582. return self._vm_firewall.description
  583. def delete(self):
  584. """
  585. Delete this VM firewall.
  586. """
  587. return self._vm_firewall.delete()
  588. def __repr__(self):
  589. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  590. self.id, self.name)
  591. class BaseVMFirewallRuleContainer(BasePageableObjectMixin,
  592. VMFirewallRuleContainer):
  593. def __init__(self, provider, firewall):
  594. self.__provider = provider
  595. self.firewall = firewall
  596. @property
  597. def _provider(self):
  598. return self.__provider
  599. def get(self, rule_id):
  600. matches = [rule for rule in self if rule.id == rule_id]
  601. if matches:
  602. return matches[0]
  603. else:
  604. return None
  605. def find(self, **kwargs):
  606. obj_list = self
  607. filters = ['name', 'direction', 'protocol', 'from_port', 'to_port',
  608. 'cidr', 'src_dest_fw', 'src_dest_fw_id']
  609. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  610. return ClientPagedResultList(self._provider, list(matches))
  611. def delete(self, rule_id):
  612. rule = self.get(rule_id)
  613. if rule:
  614. rule.delete()
  615. class BaseVMFirewallRule(BaseCloudResource, VMFirewallRule):
  616. def __init__(self, parent_fw, rule):
  617. # pylint:disable=protected-access
  618. super(BaseVMFirewallRule, self).__init__(
  619. parent_fw._provider)
  620. self.firewall = parent_fw
  621. self._rule = rule
  622. # Cache name
  623. self._name = "{0}-{1}-{2}-{3}-{4}-{5}".format(
  624. self.direction, self.protocol, self.from_port, self.to_port,
  625. self.cidr, self.src_dest_fw_id).lower()
  626. @property
  627. def name(self):
  628. return self._name
  629. def __repr__(self):
  630. return ("<{0}: id: {1}; direction: {2}; protocol: {3}; from: {4};"
  631. " to: {5}; cidr: {6}, src_dest_fw: {7}>"
  632. .format(self.__class__.__name__, self.id, self.direction,
  633. self.protocol, self.from_port, self.to_port, self.cidr,
  634. self.src_dest_fw_id))
  635. def __eq__(self, other):
  636. return (isinstance(other, VMFirewallRule) and
  637. self.direction == other.direction and
  638. self.protocol == other.protocol and
  639. self.from_port == other.from_port and
  640. self.to_port == other.to_port and
  641. self.cidr == other.cidr and
  642. self.src_dest_fw_id == other.src_dest_fw_id)
  643. def __ne__(self, other):
  644. return not self.__eq__(other)
  645. def __hash__(self):
  646. """
  647. Return a hash-based interpretation of all of the object's field values.
  648. This is requeried for operations on hashed collections including
  649. ``set``, ``frozenset``, and ``dict``.
  650. """
  651. return hash("{0}{1}{2}{3}{4}{5}".format(
  652. self.direction, self.protocol, self.from_port, self.to_port,
  653. self.cidr, self.src_dest_fw_id))
  654. def to_json(self):
  655. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  656. js = {k: v for (k, v) in attr if not k.startswith('_')}
  657. js['src_dest_fw'] = self.src_dest_fw_id
  658. js['firewall'] = self.firewall.id
  659. return js
  660. class BasePlacementZone(BaseCloudResource, PlacementZone):
  661. def __init__(self, provider):
  662. super(BasePlacementZone, self).__init__(provider)
  663. def __repr__(self):
  664. return "<CB-{0}: {1}>".format(self.__class__.__name__,
  665. self.id)
  666. def __eq__(self, other):
  667. return (isinstance(other, PlacementZone) and
  668. # pylint:disable=protected-access
  669. self._provider == other._provider and
  670. self.id == other.id)
  671. class BaseRegion(BaseCloudResource, Region):
  672. def __init__(self, provider):
  673. super(BaseRegion, self).__init__(provider)
  674. def __repr__(self):
  675. return "<CB-{0}: {1}>".format(self.__class__.__name__,
  676. self.id)
  677. def __eq__(self, other):
  678. return (isinstance(other, Region) and
  679. # pylint:disable=protected-access
  680. self._provider == other._provider and
  681. self.id == other.id)
  682. def to_json(self):
  683. attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
  684. js = {k: v for(k, v) in attr if not k.startswith('_')}
  685. js['zones'] = [z.name for z in self.zones]
  686. return js
  687. class BaseBucketObject(BaseCloudResource, BucketObject):
  688. # Regular expression for valid bucket keys.
  689. # They, must match the following criteria: http://docs.aws.amazon.com/"
  690. # AmazonS3/latest/dev/UsingMetadata.html#object-key-guidelines
  691. #
  692. # Note: The following regex is based on: https://stackoverflow.com/question
  693. # s/537772/what-is-the-most-correct-regular-expression-for-a-unix-file-path
  694. CB_NAME_PATTERN = re.compile(r"[^\0]+")
  695. def __init__(self, provider):
  696. super(BaseBucketObject, self).__init__(provider)
  697. @staticmethod
  698. def is_valid_resource_name(name):
  699. return True if BaseBucketObject.CB_NAME_PATTERN.match(name) else False
  700. @staticmethod
  701. def assert_valid_resource_name(name):
  702. if not BaseBucketObject.is_valid_resource_name(name):
  703. log.debug("InvalidNameException raised on %s", name, exc_info=True)
  704. raise InvalidNameException(
  705. u"Invalid object name: %s. Name must match criteria defined "
  706. "in: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMeta"
  707. "data.html#object-key-guidelines" % name)
  708. def save_content(self, target_stream):
  709. shutil.copyfileobj(self.iter_content(), target_stream)
  710. def __eq__(self, other):
  711. return (isinstance(other, BucketObject) and
  712. # pylint:disable=protected-access
  713. self._provider == other._provider and
  714. self.id == other.id and
  715. # check from most to least likely mutables
  716. self.name == other.name)
  717. def __repr__(self):
  718. return "<CB-{0}: {1}>".format(self.__class__.__name__,
  719. self.name)
  720. class BaseBucket(BaseCloudResource, Bucket):
  721. # Regular expression for valid bucket names.
  722. # They, must match the following criteria: http://docs.aws.amazon.com/aws
  723. # cloudtrail/latest/userguide/cloudtrail-s3-bucket-naming-requirements.html
  724. #
  725. # NOTE: The following regex is based on: https://stackoverflow.com/questio
  726. # ns/2063213/regular-expression-for-validating-dns-label-host-name
  727. CB_NAME_PATTERN = re.compile(r"^(?![0-9]+$)(?!-)[a-z0-9-]{3,63}(?<!-)$")
  728. def __init__(self, provider):
  729. super(BaseBucket, self).__init__(provider)
  730. @staticmethod
  731. def is_valid_resource_name(name):
  732. return True if BaseBucket.CB_NAME_PATTERN.match(name) else False
  733. @staticmethod
  734. def assert_valid_resource_name(name):
  735. if not BaseBucket.is_valid_resource_name(name):
  736. log.debug("Invalid resource name %s", name, exc_info=True)
  737. raise InvalidNameException(
  738. u"Invalid bucket name: %s. Name must match criteria defined "
  739. "in: http://docs.aws.amazon.com/awscloudtrail/latest/userguide"
  740. "/cloudtrail-s3-bucket-naming-requirements.html" % name)
  741. def __eq__(self, other):
  742. return (isinstance(other, Bucket) and
  743. # pylint:disable=protected-access
  744. self._provider == other._provider and
  745. self.id == other.id and
  746. # check from most to least likely mutables
  747. self.name == other.name)
  748. def __repr__(self):
  749. return "<CB-{0}: {1}>".format(self.__class__.__name__,
  750. self.name)
  751. class BaseBucketContainer(BasePageableObjectMixin, BucketContainer):
  752. def __init__(self, provider, bucket):
  753. self.__provider = provider
  754. self.bucket = bucket
  755. @property
  756. def _provider(self):
  757. return self.__provider
  758. class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
  759. CB_DEFAULT_NETWORK_NAME = os.environ.get('CB_DEFAULT_NETWORK_NAME',
  760. 'cloudbridge-net')
  761. def __init__(self, provider):
  762. super(BaseNetwork, self).__init__(provider)
  763. def __repr__(self):
  764. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  765. self.id, self.name)
  766. def wait_till_ready(self, timeout=None, interval=None):
  767. self.wait_for(
  768. [NetworkState.AVAILABLE],
  769. terminal_states=[NetworkState.ERROR],
  770. timeout=timeout,
  771. interval=interval)
  772. def create_subnet(self, name, cidr_block, zone=None):
  773. return self._provider.networking.subnets.create(
  774. name=name, network=self, cidr_block=cidr_block, zone=zone)
  775. def __eq__(self, other):
  776. return (isinstance(other, Network) and
  777. # pylint:disable=protected-access
  778. self._provider == other._provider and
  779. self.id == other.id)
  780. class BaseSubnet(BaseCloudResource, BaseObjectLifeCycleMixin, Subnet):
  781. CB_DEFAULT_SUBNET_NAME = os.environ.get('CB_DEFAULT_SUBNET_NAME',
  782. 'cloudbridge-subnet')
  783. def __init__(self, provider):
  784. super(BaseSubnet, self).__init__(provider)
  785. def __repr__(self):
  786. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  787. self.id, self.name)
  788. def __eq__(self, other):
  789. return (isinstance(other, Subnet) and
  790. # pylint:disable=protected-access
  791. self._provider == other._provider and
  792. self.id == other.id)
  793. def wait_till_ready(self, timeout=None, interval=None):
  794. self.wait_for(
  795. [SubnetState.AVAILABLE],
  796. terminal_states=[SubnetState.ERROR],
  797. timeout=timeout,
  798. interval=interval)
  799. class BaseFloatingIPContainer(FloatingIPContainer, BasePageableObjectMixin):
  800. def __init__(self, provider, gateway):
  801. self.__provider = provider
  802. self.gateway = gateway
  803. @property
  804. def _provider(self):
  805. return self.__provider
  806. def find(self, **kwargs):
  807. obj_list = self
  808. filters = ['name', 'public_ip']
  809. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  810. return ClientPagedResultList(self._provider, list(matches))
  811. def delete(self, fip_id):
  812. floating_ip = self.get(fip_id)
  813. if floating_ip:
  814. floating_ip.delete()
  815. class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
  816. def __init__(self, provider):
  817. super(BaseFloatingIP, self).__init__(provider)
  818. @property
  819. def name(self):
  820. # VM firewall rules don't support names, so pass
  821. return self.public_ip
  822. @property
  823. def state(self):
  824. return (FloatingIpState.IN_USE if self.in_use
  825. else FloatingIpState.AVAILABLE)
  826. def wait_till_ready(self, timeout=None, interval=None):
  827. self.wait_for(
  828. [FloatingIpState.AVAILABLE, FloatingIpState.IN_USE],
  829. terminal_states=[FloatingIpState.ERROR],
  830. timeout=timeout,
  831. interval=interval)
  832. def __repr__(self):
  833. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
  834. self.id, self.public_ip)
  835. def __eq__(self, other):
  836. return (isinstance(other, FloatingIP) and
  837. # pylint:disable=protected-access
  838. self._provider == other._provider and
  839. self.id == other.id)
  840. class BaseRouter(BaseCloudResource, Router):
  841. CB_DEFAULT_ROUTER_NAME = os.environ.get('CB_DEFAULT_ROUTER_NAME',
  842. 'cloudbridge-router')
  843. def __init__(self, provider):
  844. super(BaseRouter, self).__init__(provider)
  845. def __repr__(self):
  846. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__, self.id,
  847. self.name)
  848. def __eq__(self, other):
  849. return (isinstance(other, Router) and
  850. # pylint:disable=protected-access
  851. self._provider == other._provider and
  852. self.id == other.id)
  853. class BaseInternetGateway(BaseCloudResource, BaseObjectLifeCycleMixin,
  854. InternetGateway):
  855. CB_DEFAULT_INET_GATEWAY_NAME = os.environ.get(
  856. 'CB_DEFAULT_INET_GATEWAY_NAME', 'cloudbridge-inetgateway')
  857. def __init__(self, provider):
  858. super(BaseInternetGateway, self).__init__(provider)
  859. def __repr__(self):
  860. return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__, self.id,
  861. self.name)
  862. def __eq__(self, other):
  863. return (isinstance(other, InternetGateway) and
  864. # pylint:disable=protected-access
  865. self._provider == other._provider and
  866. self.id == other.id)
  867. def wait_till_ready(self, timeout=None, interval=None):
  868. self.wait_for(
  869. [GatewayState.AVAILABLE],
  870. terminal_states=[GatewayState.ERROR, GatewayState.UNKNOWN],
  871. timeout=timeout,
  872. interval=interval)