├── grails-app ├── i18n │ └── messages.properties ├── conf │ ├── UrlMappings.groovy │ ├── TrackingFilters.groovy │ ├── DataSource.groovy │ ├── Config.groovy │ └── BuildConfig.groovy └── views │ ├── dashboard │ ├── editappgroup.gsp │ ├── summary.gsp │ └── breakdown.gsp │ └── layouts │ └── main.gsp ├── screenshots ├── ss_detail.png ├── ss_summary.png ├── ss_breakdown_appgroup.png ├── ss_reservation_bytype.png └── ss_reservation_byreservation.png ├── web-app ├── images │ ├── spinner.gif │ ├── downarrowdark.gif │ ├── skin │ │ └── information.png │ └── tango │ │ ├── 16 │ │ ├── places │ │ │ └── user-trash.png │ │ ├── tools │ │ │ └── draw-freehand.png │ │ ├── actions │ │ │ └── document-save.png │ │ └── apps │ │ │ └── utilities-system-monitor.png │ │ ├── 24 │ │ ├── actions │ │ │ ├── edit-undo.png │ │ │ └── document-save.png │ │ └── places │ │ │ └── user-trash.png │ │ └── tango-icon-theme │ │ └── 32x32 │ │ └── actions │ │ ├── go-next.png │ │ └── go-previous.png ├── css │ └── ui-lightness │ │ └── images │ │ ├── animated-overlay.gif │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ └── ui-bg_highlight-soft_75_ffe45c_1x100.png └── WEB-INF │ ├── sitemesh.xml │ ├── applicationContext.xml │ └── grails.xml ├── wrapper ├── springloaded-core-1.1.1.jar ├── grails-wrapper.properties └── grails-wrapper-runtime-2.2.1.jar ├── src ├── java │ ├── log4j.properties │ ├── com │ │ └── netflix │ │ │ └── ice │ │ │ ├── reader │ │ │ ├── AggregateType.java │ │ │ ├── ThroughputMetricService.java │ │ │ ├── DataManager.java │ │ │ ├── ApplicationGroupService.java │ │ │ ├── Managers.java │ │ │ ├── ReadOnlyData.java │ │ │ ├── ApplicationGroup.java │ │ │ ├── TagGroupManager.java │ │ │ └── ReaderConfig.java │ │ │ ├── tag │ │ │ ├── ApplicationGroup.java │ │ │ ├── TagType.java │ │ │ ├── Account.java │ │ │ ├── Tag.java │ │ │ ├── ResourceGroup.java │ │ │ ├── InstanceOs.java │ │ │ ├── Product.java │ │ │ ├── Operation.java │ │ │ ├── UsageType.java │ │ │ ├── Region.java │ │ │ └── Zone.java │ │ │ ├── common │ │ │ ├── ConsolidateType.java │ │ │ ├── Randomizer.java │ │ │ ├── ProductService.java │ │ │ ├── AccountService.java │ │ │ ├── Config.java │ │ │ ├── ResourceService.java │ │ │ ├── Poller.java │ │ │ └── IceOptions.java │ │ │ ├── processor │ │ │ ├── LineItemProcessor.java │ │ │ ├── DataWriter.java │ │ │ ├── TagGroupWriter.java │ │ │ ├── ReservationService.java │ │ │ ├── ReadWriteData.java │ │ │ └── ProcessorConfig.java │ │ │ └── basic │ │ │ ├── BasicResourceService.java │ │ │ ├── BasicAccountService.java │ │ │ ├── SampleMapDbResourceService.java │ │ │ ├── MapDb.java │ │ │ ├── BasicS3ApplicationGroupService.java │ │ │ ├── BasicProductService.java │ │ │ ├── EddaResourceService.java │ │ │ ├── BasicManagers.java │ │ │ └── BasicThroughputMetricService.java │ └── sample.properties ├── groovy │ └── com │ │ └── netflix │ │ └── ice │ │ └── JSONConverter.groovy └── test │ └── com │ └── netflix │ └── ice │ └── basic │ └── EddaResourceServiceTest.java ├── .gitignore ├── application.properties ├── install.sh ├── conf └── system │ ├── setenv.sh │ └── server.xml └── grailsw.bat /grails-app/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screenshots/ss_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/screenshots/ss_detail.png -------------------------------------------------------------------------------- /screenshots/ss_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/screenshots/ss_summary.png -------------------------------------------------------------------------------- /web-app/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/spinner.gif -------------------------------------------------------------------------------- /web-app/images/downarrowdark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/downarrowdark.gif -------------------------------------------------------------------------------- /web-app/images/skin/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/skin/information.png -------------------------------------------------------------------------------- /wrapper/springloaded-core-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/wrapper/springloaded-core-1.1.1.jar -------------------------------------------------------------------------------- /screenshots/ss_breakdown_appgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/screenshots/ss_breakdown_appgroup.png -------------------------------------------------------------------------------- /screenshots/ss_reservation_bytype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/screenshots/ss_reservation_bytype.png -------------------------------------------------------------------------------- /wrapper/grails-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapper.dist.url=http://dist.springframework.org.s3.amazonaws.com/release/GRAILS/ 2 | -------------------------------------------------------------------------------- /wrapper/grails-wrapper-runtime-2.2.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/wrapper/grails-wrapper-runtime-2.2.1.jar -------------------------------------------------------------------------------- /screenshots/ss_reservation_byreservation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/screenshots/ss_reservation_byreservation.png -------------------------------------------------------------------------------- /web-app/images/tango/16/places/user-trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/16/places/user-trash.png -------------------------------------------------------------------------------- /web-app/images/tango/24/actions/edit-undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/24/actions/edit-undo.png -------------------------------------------------------------------------------- /web-app/images/tango/24/places/user-trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/24/places/user-trash.png -------------------------------------------------------------------------------- /web-app/images/tango/16/tools/draw-freehand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/16/tools/draw-freehand.png -------------------------------------------------------------------------------- /web-app/images/tango/16/actions/document-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/16/actions/document-save.png -------------------------------------------------------------------------------- /web-app/images/tango/24/actions/document-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/24/actions/document-save.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/animated-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/animated-overlay.gif -------------------------------------------------------------------------------- /web-app/images/tango/16/apps/utilities-system-monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/16/apps/utilities-system-monitor.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /web-app/images/tango/tango-icon-theme/32x32/actions/go-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/tango-icon-theme/32x32/actions/go-next.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /web-app/images/tango/tango-icon-theme/32x32/actions/go-previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/images/tango/tango-icon-theme/32x32/actions/go-previous.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /web-app/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdsol/ice/master/web-app/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /src/java/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | 3 | # stdout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} - %-5p %C{1} - [%F:%L] %m%n -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iws 2 | *Db.properties 3 | *Db.script 4 | .settings 5 | stacktrace.log 6 | *.zip 7 | /plugin.xml 8 | *.log 9 | *DB.* 10 | cobertura.ser 11 | .DS_Store 12 | target/ 13 | out/ 14 | work/ 15 | work/** 16 | web-app/plugins 17 | web-app/WEB-INF/classes 18 | .link_to_grails_plugins/ 19 | target-eclipse/ 20 | 21 | .idea 22 | *.iml 23 | -------------------------------------------------------------------------------- /web-app/WEB-INF/sitemesh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /application.properties: -------------------------------------------------------------------------------- 1 | #Grails Metadata file 2 | #Tue Sep 24 10:12:14 EDT 2013 3 | *=limitations under the License. 4 | */= 5 | /*= 6 | app.grails.version=2.2.3 7 | app.name=ice 8 | app.servlet.version=2.4 9 | app.version=1.1.0 10 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 11 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 12 | log4j.appender.stdout.layout.ConversionPattern=%d %5p [%c] %m%n 13 | log4j.category.com.amazonaws.request=WARN 14 | log4j.category.com.netflix.cloud=DEBUG 15 | log4j.category.com.netflix.logging=WARN 16 | log4j.category.com.netflix.monitoring=WARN 17 | log4j.category.hibernate=ERROR 18 | log4j.category.httpclient=ERROR 19 | log4j.category.org.codehaus.groovy=WARN 20 | log4j.category.sun.reflect=WARN 21 | log4j.rootLogger=INFO, stdout 22 | plugins.hibernate=2.2.3 23 | plugins.tomcat=2.2.3 24 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/AggregateType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | public enum AggregateType { 21 | none, 22 | stats, 23 | data, 24 | both 25 | } 26 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/ApplicationGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | public class ApplicationGroup extends Tag { 21 | 22 | public ApplicationGroup(String name) { 23 | super(name); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/TagType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | public enum TagType { 21 | Account, 22 | Region, 23 | Zone, 24 | Product, 25 | Operation, 26 | UsageType, 27 | ResourceGroup, 28 | ApplicationGroup; 29 | } 30 | -------------------------------------------------------------------------------- /grails-app/conf/UrlMappings.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class UrlMappings { 18 | 19 | static mappings = { 20 | "/$controller/$action?/$id?" { 21 | constraints { 22 | // apply constraints here 23 | } 24 | } 25 | 26 | "/" { controller = "dashboard"} 27 | "500"(view: '/error') 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/Account.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | public class Account extends Tag { 21 | public final String id; 22 | 23 | public Account(String accountId, String accountName) { 24 | super(accountName); 25 | this.id = accountId; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/ConsolidateType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | public enum ConsolidateType { 21 | hourly(3600000L), 22 | daily(24*3600000L), 23 | weekly(24*7*3600000L), 24 | monthly(-1); 25 | 26 | public final long millis; 27 | 28 | private ConsolidateType(long millis) { 29 | this.millis = millis; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/ThroughputMetricService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import com.netflix.ice.common.ConsolidateType; 21 | import org.joda.time.Interval; 22 | 23 | /** 24 | * Interface to provide throughput metrics. 25 | */ 26 | public interface ThroughputMetricService { 27 | void init(); 28 | String getMetricName(); 29 | String getMetricUnitName(); 30 | String getFactoredCostCurrencySign(); 31 | double getFactoredCostMultiply(); 32 | double[] getData(Interval interval, ConsolidateType consolidateType) throws Exception; 33 | } 34 | -------------------------------------------------------------------------------- /grails-app/conf/TrackingFilters.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.util.regex.Pattern 17 | import org.joda.time.DateTime 18 | 19 | class TrackingFilters { 20 | 21 | static final Pattern nonBrowserUserAgents = Pattern.compile( 22 | '.*(libcurl|Python-urllib|Wget|HttpClient|lwp-request|Java).*') 23 | 24 | static filters = { 25 | allExceptError(controller: 'error', invert: true) { 26 | before = { 27 | 28 | String userAgent = request.getHeader('user-agent') 29 | if (userAgent?.contains('MSIE') && !userAgent.contains('chromeframe')) { 30 | request['ieWithoutChromeFrame'] = true 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/Randomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * Used to generate random/fake data 24 | */ 25 | public interface Randomizer { 26 | 27 | /** 28 | * Get random/fake usage for given tag group. 29 | * @param time 30 | * @param tagGroup 31 | * @param usage 32 | * @return random/fake usage 33 | */ 34 | double randomizeUsage(long time, TagGroup tagGroup, double usage); 35 | 36 | /** 37 | * Get random/fake cost for given tag group. 38 | * @param tagGroup 39 | * @return random/fake cost 40 | */ 41 | double randomizeCost(TagGroup tagGroup); 42 | 43 | /** 44 | * Get map of resource distribution. 45 | * @return map of resource distribution 46 | */ 47 | Map getDistribution(TagGroup tagGroup); 48 | } 49 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/DataManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import com.netflix.ice.tag.Tag; 21 | import com.netflix.ice.tag.TagType; 22 | import org.joda.time.DateTime; 23 | import org.joda.time.Interval; 24 | 25 | import java.util.Map; 26 | 27 | /** 28 | * Interface to feed data to UI. 29 | */ 30 | public interface DataManager { 31 | 32 | /** 33 | * Get map of data. 34 | * @param interval 35 | * @param tagLists 36 | * @param groupBy 37 | * @param aggregate 38 | * @param forReservation 39 | * @return 40 | */ 41 | Map getData(Interval interval, TagLists tagLists, TagType groupBy, AggregateType aggregate, boolean forReservation); 42 | 43 | /** 44 | * Get data length. 45 | * @param start 46 | * @return 47 | */ 48 | int getDataLength(DateTime start); 49 | } 50 | -------------------------------------------------------------------------------- /src/groovy/com/netflix/ice/JSONConverter.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice 19 | 20 | import grails.converters.JSON 21 | import com.netflix.ice.tag.Tag 22 | import com.netflix.ice.reader.ApplicationGroup 23 | 24 | class JSONConverter { 25 | 26 | static void register() { 27 | JSON.registerObjectMarshaller(Tag) { Tag it -> 28 | return [name: it.name] 29 | } 30 | JSON.registerObjectMarshaller(ApplicationGroup) { ApplicationGroup appgroup -> 31 | def result = [name: appgroup.name, owner: appgroup.owner, data: [:]] 32 | for (String key: appgroup.data.keySet()) { 33 | result.data[key] = [] 34 | for (String v: appgroup.data.get(key)) { 35 | result.data[key].add([name: v]) 36 | } 37 | } 38 | return result 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /grails-app/conf/DataSource.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /*dataSource { 17 | pooled = true 18 | driverClassName = "org.hsqldb.jdbcDriver" 19 | username = "sa" 20 | password = "" 21 | }*/ 22 | hibernate { 23 | cache.use_second_level_cache = true 24 | cache.use_query_cache = true 25 | cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider' 26 | } 27 | 28 | // environment specific settings 29 | environments { 30 | development { 31 | dataSource { 32 | dbCreate = "create-drop" // one of 'create', 'create-drop','update' 33 | //url = "jdbc:hsqldb:mem:devDB" 34 | } 35 | } 36 | test { 37 | dataSource { 38 | dbCreate = "update" 39 | //url = "jdbc:hsqldb:mem:testDb" 40 | } 41 | } 42 | production { 43 | dataSource { 44 | dbCreate = "update" 45 | //url = "jdbc:hsqldb:file:prodDb;shutdown=true" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web-app/WEB-INF/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Grails application factory bean 9 | 10 | 11 | 12 | 13 | 14 | A bean that manages Grails plugins 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | utf-8 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/ProductService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | import com.netflix.ice.tag.Product; 21 | 22 | import java.util.Collection; 23 | import java.util.List; 24 | 25 | public interface ProductService { 26 | 27 | /** 28 | * Get product by aws name, e.g. "Amazon Elastic Compute Cloud", "AWS Elastic MapReduce" 29 | * @param name 30 | * @return product 31 | */ 32 | Product getProductByAwsName(String name); 33 | 34 | /** 35 | * Get product by name, e.g. ec2, emr 36 | * @param name 37 | * @return 38 | */ 39 | Product getProductByName(String name); 40 | 41 | /** 42 | * Get list of products from given names 43 | * @param names 44 | * @return list of products 45 | */ 46 | public List getProducts(List names); 47 | 48 | /** 49 | * Get list of products 50 | * @return list of products 51 | */ 52 | public Collection getProducts(); 53 | } 54 | -------------------------------------------------------------------------------- /web-app/WEB-INF/grails.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | DefaultSecurityConfig 22 | grails.plugins.springsecurity.SpringSecurityService 23 | grails.plugins.springsecurity.SecurityTagLib 24 | DefaultLdapSecurityConfig 25 | BootStrap 26 | BuildConfig 27 | Config 28 | DataSource 29 | OccasionFilters 30 | RegionFilters 31 | SettingsFilters 32 | resources 33 | TrackingFilters 34 | UrlMappings 35 | com.netflix.ice.DashboardController 36 | 37 | 38 | HibernateGrailsPlugin 39 | SpringSecurityCoreGrailsPlugin 40 | SpringSecurityLdapGrailsPlugin 41 | 42 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/Tag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | import java.io.Serializable; 21 | 22 | public abstract class Tag implements Comparable, Serializable { 23 | public static final Tag aggregated = new Tag("aggregated") { 24 | @Override 25 | public int compareTo(Tag t) { 26 | return this == t ? 0 : -1; 27 | } 28 | }; 29 | 30 | public final String name; 31 | Tag(String name) { 32 | this.name = name; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (o instanceof Tag) 38 | return this.name.equals(((Tag)o).name); 39 | else 40 | return false; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.name; 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return this.name.hashCode(); 51 | } 52 | 53 | public int compareTo(Tag t) { 54 | if (t == aggregated) 55 | return -t.compareTo(this); 56 | int result = ("a" + this.name).compareTo("a" + t.name); 57 | return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/ApplicationGroupService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * Interface to manager user defined application groups. 24 | */ 25 | public interface ApplicationGroupService { 26 | 27 | void init(); 28 | 29 | /** 30 | * Get a map of application groups. Key will be the application group name. 31 | * @return map of application groups 32 | */ 33 | Map getApplicationGroups(); 34 | 35 | /** 36 | * Get application group by name. 37 | * @param name 38 | * @return application group or null if not exist 39 | */ 40 | ApplicationGroup getApplicationGroup(String name); 41 | 42 | /** 43 | * Save application group configuration. 44 | * @param applicationGroup 45 | * @return whether or not save was successfull 46 | */ 47 | boolean saveApplicationGroup(ApplicationGroup applicationGroup); 48 | 49 | /** 50 | * Delete application group. 51 | * @param name 52 | * @return whether or not delete was successfull 53 | */ 54 | boolean deleteApplicationGroup(String name); 55 | } 56 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/processor/LineItemProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.processor; 19 | 20 | import com.netflix.ice.tag.Product; 21 | import java.util.List; 22 | import org.joda.time.DateTimeZone; 23 | import org.joda.time.format.DateTimeFormat; 24 | import org.joda.time.format.DateTimeFormatter; 25 | 26 | import java.util.Map; 27 | 28 | /** 29 | * Interface to process each line item in billing file. 30 | */ 31 | public interface LineItemProcessor { 32 | public static final DateTimeFormatter amazonBillingDateFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeZone.UTC); 33 | public static final DateTimeFormatter amazonBillingDateFormat2 = DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss").withZone(DateTimeZone.UTC); 34 | 35 | void initIndexes(boolean withTags, String[] header); 36 | List getHeader(); 37 | int getUserTagStartIndex(); 38 | long getEndMillis(String[] items); 39 | Result process(long startMilli, boolean processAll, ProcessorConfig config, String[] items, Map usageDataByProduct, Map costDataByProduct, Map ondemandRate); 40 | 41 | public static enum Result { 42 | delay, 43 | ignore, 44 | hourly, 45 | monthly, 46 | daily 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/ResourceGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | import com.google.common.collect.Lists; 21 | import com.google.common.collect.Maps; 22 | 23 | import java.util.List; 24 | import java.util.concurrent.ConcurrentMap; 25 | 26 | public class ResourceGroup extends Tag { 27 | private ResourceGroup (String name) { 28 | super(name); 29 | } 30 | private static ConcurrentMap resourceGroups = Maps.newConcurrentMap(); 31 | 32 | public static ResourceGroup getResourceGroup(String name) { 33 | ResourceGroup resourceGroup = resourceGroups.get(name); 34 | if (resourceGroup == null) { 35 | resourceGroups.putIfAbsent(name, new ResourceGroup(name)); 36 | resourceGroup = resourceGroups.get(name); 37 | } 38 | return resourceGroup; 39 | } 40 | 41 | public static List getResourceGroups(List names) { 42 | List result = Lists.newArrayList(); 43 | if (names != null) { 44 | for (String name: names) { 45 | ResourceGroup resourceGroup = resourceGroups.get(name); 46 | if (resourceGroup != null) 47 | result.add(resourceGroup); 48 | } 49 | } 50 | return result; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/Managers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import com.netflix.ice.common.*; 21 | import com.netflix.ice.tag.Product; 22 | 23 | import java.util.Collection; 24 | 25 | /** 26 | * Interface to manager all TagGroupManager and DataManager instances for different products 27 | */ 28 | public interface Managers { 29 | 30 | void init(); 31 | 32 | /** 33 | * 34 | * @return collection of products 35 | */ 36 | Collection getProducts(); 37 | 38 | /** 39 | * 40 | * @param product 41 | * @return TagGroupManager instance for specified product 42 | */ 43 | TagGroupManager getTagGroupManager(Product product); 44 | 45 | /** 46 | * 47 | * @param product 48 | * @param consolidateType 49 | * @return cost DataManager instance for specified product and consolidateType 50 | */ 51 | DataManager getCostManager(Product product, ConsolidateType consolidateType); 52 | 53 | /** 54 | * 55 | * @param product 56 | * @param consolidateType 57 | * @return usage DataManager instance for specified product and consolidateType 58 | */ 59 | DataManager getUsageManager(Product product, ConsolidateType consolidateType); 60 | 61 | /** 62 | * shutdown all manager instances 63 | */ 64 | void shutdown(); 65 | } 66 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/InstanceOs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | public enum InstanceOs { 21 | linux("", "", "Linux/UNIX"), 22 | sqlserverweb(".sqlserverweb", ":0202", "Windows with SQL Server Web"), 23 | sqlserverstd(".sqlserverstd", ":0006", "Windows with SQL Server Standard"), 24 | sles(".sles", ":000g", "SUSE Linux"), 25 | rhel(".rhel", ":0010", "Red Hat Enterprise Linux"), 26 | rhbl(".rhbl", ":00g0", "Red Hat BYOL Linux"), 27 | windows(".windows", ":0002", "Windows"), 28 | dw(".dw", ":0001", "redshift"), 29 | others(".others", ":others", "others"); 30 | 31 | public final String usageType; 32 | public final String code; 33 | public final String description; 34 | 35 | InstanceOs(String usageType, String code, String description) { 36 | this.usageType = usageType; 37 | this.code = code.toLowerCase(); 38 | this.description = description.toLowerCase(); 39 | } 40 | 41 | public static InstanceOs withCode(String code) { 42 | for (InstanceOs os: InstanceOs.values()) { 43 | if (code.toLowerCase().equals(os.code)) 44 | return os; 45 | } 46 | return others; 47 | } 48 | 49 | public static InstanceOs withDescription(String description) { 50 | for (InstanceOs os: InstanceOs.values()) { 51 | if (description.toLowerCase().startsWith(os.description)) 52 | return os; 53 | } 54 | return others; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/Product.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | public class Product extends Tag { 21 | public static final Product cloudfront = new Product("cloudfront"); 22 | public static final Product cloudhsm = new Product("cloudhsm"); 23 | public static final Product cloudwatch = new Product("cloudwatch"); 24 | public static final Product data_transfer = new Product("data_transfer"); 25 | public static final Product direct_connect = new Product("direct_connect"); 26 | public static final Product dynamodb = new Product("dynamodb"); 27 | public static final Product ebs = new Product("ebs"); 28 | public static final Product ec2 = new Product("ec2"); 29 | public static final Product ec2_instance = new Product("ec2_instance"); 30 | public static final Product eip = new Product("eip"); 31 | public static final Product elasticache = new Product("elasticache"); 32 | public static final Product emr = new Product("emr"); 33 | public static final Product glacier = new Product("glacier"); 34 | public static final Product monitor = new Product("monitor"); 35 | public static final Product rds = new Product("rds"); 36 | public static final Product redshift = new Product("redshift"); 37 | public static final Product route53 = new Product("route53"); 38 | public static final Product s3 = new Product("s3"); 39 | public static final Product simpledb = new Product("simpledb"); 40 | public static final Product ses = new Product("ses"); 41 | public static final Product sns = new Product("sns"); 42 | public static final Product sqs = new Product("sqs"); 43 | public static final Product sws = new Product("sws"); 44 | public static final Product vpc = new Product("vpc"); 45 | 46 | public Product (String name) { 47 | super(name); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/ReadOnlyData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import com.google.common.collect.Lists; 21 | import com.netflix.ice.common.TagGroup; 22 | 23 | import java.io.DataInput; 24 | import java.io.IOException; 25 | import java.util.Collection; 26 | import java.util.List; 27 | 28 | public class ReadOnlyData { 29 | double[][] data; 30 | private Collection tagGroups; 31 | 32 | public ReadOnlyData(double[][] data, Collection tagGroups) { 33 | this.data = data; 34 | this.tagGroups = tagGroups; 35 | } 36 | 37 | public double[] getData(int i) { 38 | return data[i]; 39 | } 40 | 41 | public int getNum() { 42 | return data.length; 43 | } 44 | 45 | public Collection getTagGroups() { 46 | return tagGroups; 47 | } 48 | 49 | public static class Serializer { 50 | 51 | public static ReadOnlyData deserialize(DataInput in) throws IOException { 52 | 53 | int numKeys = in.readInt(); 54 | List keys = Lists.newArrayList(); 55 | for (int j = 0; j < numKeys; j++) { 56 | keys.add(TagGroup.Serializer.deserialize(ReaderConfig.getInstance(), in)); 57 | } 58 | 59 | int num = in.readInt(); 60 | double[][] data = new double[num][]; 61 | for (int i = 0; i < num; i++) { 62 | data[i] = new double[keys.size()]; 63 | boolean hasData = in.readBoolean(); 64 | if (hasData) { 65 | for (int j = 0; j < keys.size(); j++) { 66 | double v = in.readDouble(); 67 | if (v != 0) { 68 | data[i][j] = v; 69 | } 70 | } 71 | } 72 | } 73 | 74 | return new ReadOnlyData(data, keys); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get Ice in a ready-to-run state on a fresh AWI instance. 4 | 5 | GRAILS_VERSION=2.2.1 6 | 7 | # Install prerequisites 8 | sudo yum -y install git java-1.6.0-openjdk-devel.x86_64 wget 9 | 10 | INSTALL_DIR=$(pwd) 11 | 12 | cd 13 | 14 | HOME_DIR=$(pwd) 15 | 16 | # Prep grails in such a way that we only download it once 17 | if [ ! -x ".grails/wrapper/${GRAILS_VERSION}/grails-${GRAILS_VERSION}" ]; then 18 | mkdir -p .grails/wrapper/ 19 | cd .grails/wrapper/ 20 | # (Fetch) 21 | wget http://dist.springframework.org.s3.amazonaws.com/release/GRAILS/grails-${GRAILS_VERSION}.zip -O grails-${GRAILS_VERSION}-download.zip 22 | mkdir ${GRAILS_VERSION} 23 | cd ${GRAILS_VERSION} 24 | # ("Install") 25 | unzip ../grails-${GRAILS_VERSION}-download.zip 26 | fi 27 | 28 | GRAILS_HOME=${HOME_DIR}/.grails/wrapper/${GRAILS_VERSION}/grails-${GRAILS_VERSION}/ 29 | PATH=$PATH:${HOME_DIR}/.grails/wrapper/${GRAILS_VERSION}/grails-${GRAILS_VERSION}/bin/ 30 | 31 | # Get ice 32 | cd ${INSTALL_DIR} 33 | 34 | if [ -x '.git' ]; then 35 | # We already have it; update to latest git 36 | git pull 37 | else 38 | # We don't have it at all yet; clone the repo 39 | git clone https://github.com/Netflix/ice.git 40 | cd ice 41 | INSTALL_DIR=$(pwd) 42 | fi 43 | 44 | # Initialize Ice with Grails 45 | grails ${JAVA_OPTS} wrapper 46 | 47 | # (Bug: Ice can't deal with this file existing and being empty.) 48 | rm grails-app/i18n/messages.properties 49 | 50 | # Create our local work directories (both for processing and reading) 51 | mkdir ${HOME_DIR}/ice_processor 52 | mkdir ${HOME_DIR}/ice_reader 53 | 54 | # Set up the config file 55 | cp src/java/sample.properties src/java/ice.properties 56 | echo Please enter the name of the bucket Ice will read Amazon billing information from: 57 | while [ "$BILLBUCKET" == "" ] 58 | do 59 | echo -n "-> " 60 | read -r BILLBUCKET 61 | done 62 | 63 | echo Please enter the name of the bucket Ice will write processed billing information to: 64 | while [ "$PROCBUCKET" == "" ] 65 | do 66 | echo -n "-> " 67 | read -r PROCBUCKET 68 | done 69 | sed -rie 's/=billing_s3bucketprefix\//=/; s/\/mnt\//\/home\/ec2-user\//; s/=work_s3bucketprefix\//=/; s/^ice.account.*//; s/=billing_s3bucketname/='${BILLBUCKET}'/; s/=work_s3bucketname/='${PROCBUCKET}'/' src/java/ice.properties 70 | 71 | echo Ice is now ready to run as a processor. If you want to run the reader, edit: 72 | echo ~/ice/src/java/ice.properties 73 | echo and alter the appropriate flags. You can now start Ice by running the following from the Ice root directory: 74 | echo ./grailsw -Djava.net.preferIPv4Stack=true -Dice.s3AccessKeyId=\ -Dice.s3SecretKey=\ run-app 75 | -------------------------------------------------------------------------------- /conf/system/setenv.sh: -------------------------------------------------------------------------------- 1 | # Determine the max heap size based on the instance type. 2 | # See: http://aws.amazon.com/ec2/instance-types/ 3 | # 4 | # For nodes in the datacenter we rely on the convention that the hostname 5 | # indicates the amount of memory. 6 | function heapSize { 7 | instanceType=$(hostname) 8 | if [ "$EC2_INSTANCE_TYPE" != "" ]; then 9 | instanceType="$EC2_INSTANCE_TYPE" 10 | fi 11 | 12 | case $instanceType in 13 | m1.small) echo "1g" ;; # 1.7GB 14 | m1.large) echo "6g" ;; # 7.5GB 15 | m1.xlarge) echo "12g" ;; # 15GB 16 | t1.micro) echo "256m" ;; # 613MB 17 | m2.xlarge) echo "14g" ;; # 17.1GB 18 | m2.2xlarge) echo "30g" ;; # 34.2GB 19 | m2.4xlarge) echo "60g" ;; # 68.4GB 20 | c1.medium) echo "1g" ;; # 1.7GB 21 | c1.xlarge) echo "4g" ;; # 7GB 22 | cc1.4xlarge) echo "20g" ;; # 23GB 23 | cg1.4xlarge) echo "20g" ;; # 22GB 24 | *-4g*) echo "3g" ;; 25 | *-8g*) echo "6g" ;; 26 | *-16g*) echo "14g" ;; 27 | *-24g*) echo "21g" ;; 28 | *-32g*) echo "28g" ;; 29 | *-64g*) echo "60g" ;; 30 | *-72g*) echo "68g" ;; 31 | *) echo "1g" ;; # who knows, try for a gig 32 | esac 33 | } 34 | 35 | # Create the java heap size options. We always set min and max heap size to 36 | # be the same, this ensures that the program will crash right away if there 37 | # is not enough memory. 38 | function javaOptsHeapSize { 39 | permPercent=$1 40 | max=$(heapSize) 41 | permSize="$(echo "$max" | awk "{print int(\$1 * $permPercent)}")g" 42 | echo "-Xms$max -Xmx$max -XX:PermSize=$permSize -XX:MaxPermSize=$permSize" 43 | } 44 | 45 | GCLOG=/apps/tomcat/logs/gc.log 46 | CATALINA_PID=/apps/tomcat/logs/catalina.pid 47 | JRE_HOME=/apps/java 48 | JAVA_HOME=$JRE_HOME 49 | if [ "$1" == "start" ]; then 50 | JAVA_OPTS=" \ 51 | -javaagent:/apps/appagent/javaagent.jar \ 52 | -verbosegc \ 53 | -XX:+PrintGCDetails \ 54 | -XX:+PrintGCTimeStamps \ 55 | -Xloggc:$GCLOG \ 56 | $(javaOptsHeapSize 0.1) \ 57 | -Xss8192k \ 58 | -Dnetflix.logging.realtimetracers=false \ 59 | -Dlog4j.appender.RTA=org.apache.log4j.varia.NullAppender \ 60 | -Dlog4j.appender.CHUKWA=org.apache.log4j.varia.NullAppender \ 61 | -Dlog4j.appender.CHUKWA.threshold=OFF \ 62 | -Dlog4j.logger.net.spy.memcached.internal.OperationFuture=FATAL \ 63 | -Dnet.spy.log.LoggerImpl=net.spy.memcached.compat.log.Log4JLogger \ 64 | -Dnetflix.environment=test 65 | -Dcom.sun.management.jmxremote.ssl=false \ 66 | -Dcom.sun.management.jmxremote.port=7500 \ 67 | -Dcom.sun.management.jmxremote.authenticate=false \ 68 | -Dnetflix.appinfo.name=${NETFLIX_APP}" 69 | 70 | logrotate -f /apps/tomcat/bin/.logrotate.conf 71 | else 72 | JAVA_OPTS="" 73 | fi 74 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/processor/DataWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.processor; 19 | 20 | import com.netflix.ice.common.AwsUtils; 21 | import com.netflix.ice.common.TagGroup; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.io.*; 26 | import java.util.Collection; 27 | import java.util.TreeMap; 28 | 29 | public class DataWriter { 30 | private final static Logger logger = LoggerFactory.getLogger(DataWriter.class); 31 | 32 | 33 | private TreeMap> tagGroups; 34 | private ProcessorConfig config = ProcessorConfig.getInstance(); 35 | private String dbName; 36 | private File file; 37 | private ReadWriteData data; 38 | 39 | DataWriter(String name, boolean loadData) throws Exception { 40 | 41 | dbName = name; 42 | file = new File(config.localDir, dbName); 43 | if (loadData) { 44 | AwsUtils.downloadFileIfNotExist(config.workS3BucketName, config.workS3BucketPrefix, file); 45 | } 46 | 47 | if (file.exists()) { 48 | DataInputStream in = new DataInputStream(new FileInputStream(file)); 49 | try { 50 | data = ReadWriteData.Serializer.deserialize(in); 51 | } 52 | finally { 53 | in.close(); 54 | } 55 | } 56 | else { 57 | data = new ReadWriteData(); 58 | } 59 | } 60 | 61 | ReadWriteData getData() { 62 | return data; 63 | } 64 | 65 | void archive() throws IOException { 66 | archive(data); 67 | } 68 | 69 | void archive(ReadWriteData data) throws IOException { 70 | 71 | DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); 72 | try { 73 | ReadWriteData.Serializer.serialize(out, data); 74 | } 75 | finally { 76 | out.close(); 77 | } 78 | 79 | logger.info(this.dbName + " uploading to s3..."); 80 | AwsUtils.upload(config.workS3BucketName, config.workS3BucketPrefix, config.localDir, dbName); 81 | logger.info(this.dbName + " uploading done."); 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/basic/BasicResourceService.java: -------------------------------------------------------------------------------- 1 | package com.netflix.ice.basic; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.netflix.ice.common.ResourceService; 5 | import com.netflix.ice.processor.ProcessorConfig; 6 | import com.netflix.ice.reader.ReaderConfig; 7 | import com.netflix.ice.tag.Account; 8 | import com.netflix.ice.tag.Product; 9 | import com.netflix.ice.tag.Region; 10 | import org.apache.commons.lang.StringUtils; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.List; 15 | 16 | public class BasicResourceService extends ResourceService { 17 | protected Logger logger = LoggerFactory.getLogger(BasicResourceService.class); 18 | private ProcessorConfig processorConfig; 19 | public static final String MEDISTRANO_TESTS = "medistrano_tests"; 20 | 21 | @Override 22 | public void init() { 23 | logger.info("Retrieving processor config for custom BasicResourceService"); 24 | processorConfig = ProcessorConfig.getInstance(); 25 | } 26 | 27 | @Override 28 | public String getResource(Account account, Region region, Product product, String resourceId, String[] lineItem, long millisStart) { 29 | List header = processorConfig.lineItemProcessor.getHeader(); 30 | 31 | // Use bucket name for S3 resources 32 | if (product == Product.s3) { 33 | return resourceId.toLowerCase(); 34 | } 35 | 36 | String productName = ""; 37 | String result = ""; 38 | int productIndex = header.indexOf("user:Product"); 39 | 40 | if (productIndex > 0 && lineItem.length > productIndex && StringUtils.isNotEmpty(lineItem[productIndex])) { 41 | productName = lineItem[productIndex]; 42 | 43 | if (productName.contains("ztestz")) { 44 | return MEDISTRANO_TESTS; 45 | } else { 46 | for (String tag: processorConfig.customTags) { 47 | int index = header.indexOf(tag); 48 | if (index > 0 && lineItem.length > index && StringUtils.isNotEmpty(lineItem[index])) { 49 | result = StringUtils.isEmpty(result) ? lineItem[index] : result + "_" + lineItem[index]; 50 | } 51 | } 52 | } 53 | } 54 | 55 | return StringUtils.isEmpty(result) ? product.name.toLowerCase() : result.toLowerCase(); 56 | } 57 | 58 | @Override 59 | public List> getProductsWithResources() { 60 | List> result = Lists.newArrayList(); 61 | for (Product product: ReaderConfig.getInstance().productService.getProducts()) { 62 | result.add(Lists.newArrayList(product)); 63 | } 64 | return result; 65 | } 66 | 67 | @Override 68 | public void commit() { 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/processor/TagGroupWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.processor; 19 | 20 | import com.google.common.collect.Maps; 21 | import com.netflix.ice.common.AwsUtils; 22 | import com.netflix.ice.common.TagGroup; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.io.*; 27 | import java.util.Collection; 28 | import java.util.TreeMap; 29 | 30 | public class TagGroupWriter { 31 | private final static Logger logger = LoggerFactory.getLogger(TagGroupWriter.class); 32 | public final static String DB_PREFIX = "tagdb_"; 33 | 34 | private TreeMap> tagGroups; 35 | private ProcessorConfig config = ProcessorConfig.getInstance(); 36 | private String dbName; 37 | private File file; 38 | 39 | TagGroupWriter(String name) throws Exception { 40 | 41 | dbName = DB_PREFIX + name; 42 | file = new File(config.localDir, dbName); 43 | logger.info("creating TagGroupWriter for " + file); 44 | AwsUtils.downloadFileIfNotExist(config.workS3BucketName, config.workS3BucketPrefix, file); 45 | 46 | if (file.exists()) { 47 | DataInputStream in = new DataInputStream(new FileInputStream(file)); 48 | try { 49 | tagGroups = TagGroup.Serializer.deserializeTagGroups(config, in); 50 | } 51 | finally { 52 | if (in != null) 53 | in.close(); 54 | } 55 | } 56 | else { 57 | tagGroups = Maps.newTreeMap(); 58 | } 59 | } 60 | 61 | void archive(Long monthMilli,Collection tagGroups) throws IOException { 62 | this.tagGroups.put(monthMilli, tagGroups); 63 | 64 | DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); 65 | try { 66 | TagGroup.Serializer.serializeTagGroups(out, this.tagGroups); 67 | } 68 | finally { 69 | out.close(); 70 | } 71 | 72 | logger.info(dbName + " uploading to s3..."); 73 | AwsUtils.upload(config.workS3BucketName, config.workS3BucketPrefix, config.localDir, dbName); 74 | logger.info(dbName + " uploading done."); 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/processor/ReservationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.processor; 19 | 20 | import com.amazonaws.services.ec2.model.ReservedInstances; 21 | import com.netflix.ice.common.TagGroup; 22 | import com.netflix.ice.tag.Region; 23 | import com.netflix.ice.tag.UsageType; 24 | 25 | import java.util.Collection; 26 | import java.util.Map; 27 | 28 | /** 29 | * Interface for reservations. 30 | */ 31 | public interface ReservationService { 32 | 33 | void init(); 34 | 35 | /** 36 | * Get all tag groups with reservations 37 | * @return 38 | */ 39 | Collection getTaGroups(); 40 | 41 | /** 42 | * 43 | * @return 44 | */ 45 | Ec2InstanceReservationPrice.ReservationPeriod getReservationPeriod(); 46 | 47 | /** 48 | * 49 | * @return 50 | */ 51 | Ec2InstanceReservationPrice.ReservationUtilization getReservationUtilization(); 52 | 53 | /** 54 | * Get reservation info. 55 | * @param time 56 | * @param tagGroup 57 | * @return 58 | */ 59 | ReservationInfo getReservation( 60 | long time, 61 | TagGroup tagGroup); 62 | 63 | /** 64 | * Some companies may get different price tiers at different times depending on reservation cost. 65 | * This method is to get the latest hourly price including amortized upfront for given, time, region and usage type. 66 | * @param time 67 | * @param region 68 | * @param usageType 69 | * @return 70 | */ 71 | double getLatestHourlyTotalPrice( 72 | long time, 73 | Region region, 74 | UsageType usageType); 75 | 76 | /** 77 | * Called by ReservationCapacityPoller to update reservations. 78 | * @param reservations 79 | */ 80 | void updateEc2Reservations(Map reservations); 81 | 82 | 83 | public static class ReservationInfo { 84 | public final int capacity; 85 | public final double upfrontAmortized; 86 | public final double reservationHourlyCost; 87 | 88 | public ReservationInfo (int capacity, double upfrontAmortized, double reservationHourlyCost) { 89 | this.capacity = capacity; 90 | this.upfrontAmortized = upfrontAmortized; 91 | this.reservationHourlyCost = reservationHourlyCost; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/Operation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | import com.google.common.collect.Lists; 21 | import com.google.common.collect.Maps; 22 | 23 | import java.util.List; 24 | import java.util.concurrent.ConcurrentMap; 25 | 26 | public class Operation extends Tag { 27 | 28 | protected int seq = Integer.MAX_VALUE; 29 | private Operation (String name) { 30 | super(name); 31 | } 32 | private static ConcurrentMap operations = Maps.newConcurrentMap(); 33 | 34 | public static final ReservationOperation ondemandInstances = new ReservationOperation("OndemandInstances", 0); 35 | public static final ReservationOperation reservedInstances = new ReservationOperation("ReservedInstances", 1); 36 | public static final ReservationOperation borrowedInstances = new ReservationOperation("BorrowedInstances", 2); 37 | public static final ReservationOperation lentInstances = new ReservationOperation("LentInstances", 3); 38 | public static final ReservationOperation unusedInstances = new ReservationOperation("UnusedInstances", 4); 39 | public static final ReservationOperation upfrontAmortized = new ReservationOperation("UpfrontAmortized", 5); 40 | 41 | public static Operation getOperation(String name) { 42 | 43 | Operation operation = operations.get(name); 44 | if (operation == null) { 45 | operations.putIfAbsent(name, new Operation(name)); 46 | operation = operations.get(name); 47 | } 48 | 49 | return operation; 50 | } 51 | 52 | public static List getOperations(List names) { 53 | List result = Lists.newArrayList(); 54 | for (String name: names) 55 | result.add(operations.get(name)); 56 | return result; 57 | } 58 | 59 | public static class ReservationOperation extends Operation { 60 | private ReservationOperation(String name, int seq) { 61 | super(name); 62 | this.seq = seq; 63 | operations.put(name, this); 64 | } 65 | } 66 | 67 | @Override 68 | public int compareTo(Tag t) { 69 | if (t instanceof Operation) { 70 | Operation o = (Operation)t; 71 | int result = this.seq - o.seq; 72 | return result == 0 ? this.name.compareTo(t.name) : result; 73 | } 74 | else 75 | return super.compareTo(t); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/AccountService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | import com.netflix.ice.tag.Account; 21 | import com.netflix.ice.tag.Zone; 22 | 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | public interface AccountService { 27 | /** 28 | * Get account by AWS id. The AWS id is usually an un-readable 12 digit string. 29 | * @param accountId 30 | * @return Account object associated with the account id 31 | */ 32 | Account getAccountById(String accountId); 33 | 34 | /** 35 | * Get account by account name. The account name is a user defined readable string. 36 | * @param accountName 37 | * @return Account object associated with the account name 38 | */ 39 | Account getAccountByName(String accountName); 40 | 41 | /** 42 | * Get a list of accounts from given account names. 43 | * @param accountNames 44 | * @return List of accounts 45 | */ 46 | List getAccounts(List accountNames); 47 | 48 | /** 49 | * If you don't have reserved instances, you can return an empty map. 50 | * @return Map of accounts. The keys are owner accounts, the values are list of borrowing accounts. 51 | */ 52 | Map> getReservationAccounts(); 53 | 54 | /** 55 | * If you don't need to poll reservation capacity through ec2 API for other accounts, you can return an empty map. 56 | * @return Map of account access roles. The keys are reservation owner accounts, 57 | * the values are assumed roles to call ec2 describeReservedInstances on each reservation owner account. 58 | */ 59 | Map getReservationAccessRoles(); 60 | 61 | /** 62 | * If you don't need to poll reservation capacity through ec2 API for other accounts, ir if you don't use external ids, 63 | * you can return an empty map. 64 | * @return Map of account access external ids. The keys are reservation owner accounts, 65 | * the values are external ids to call ec2 describeReservedInstances on each reservation owner account. 66 | */ 67 | Map getReservationAccessExternalIds(); 68 | 69 | /** 70 | * If you don't share reserved instances among multiple accounts, you can return the input zone. 71 | * @param mapAccount 72 | * @param account 73 | * @param zone 74 | * @return Mapped zone of mapAccount. 75 | */ 76 | Zone getAccountMappedZone(Account mapAccount, Account account, Zone zone); 77 | } 78 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/reader/ApplicationGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.reader; 19 | 20 | import com.google.common.collect.Lists; 21 | import com.google.common.collect.Maps; 22 | import org.json.JSONArray; 23 | import org.json.JSONException; 24 | import org.json.JSONObject; 25 | import org.json.JSONTokener; 26 | 27 | import java.util.Iterator; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | public class ApplicationGroup { 32 | public final Map> data; 33 | public final String name; 34 | public final String owner; 35 | 36 | // todo: remove this constructor 37 | public ApplicationGroup(String name, String owner, List ec2s, List s3s) { 38 | this.name = name; 39 | this.owner = owner; 40 | this.data = Maps.newHashMap(); 41 | this.data.put("rds", Lists.newArrayList()); 42 | this.data.put("ec2", ec2s); 43 | this.data.put("s3", s3s); 44 | } 45 | 46 | public ApplicationGroup(String name, String owner, Map> data) { 47 | this.name = name; 48 | this.owner = owner; 49 | this.data = data; 50 | } 51 | 52 | public ApplicationGroup(String jsonStr) throws JSONException { 53 | data = Maps.newHashMap(); 54 | JSONObject json = new JSONObject(new JSONTokener(jsonStr)); 55 | name = json.getString("name"); 56 | owner = json.getString("owner"); 57 | json = json.getJSONObject("data"); 58 | Iterator keys = json.keys(); 59 | while (keys.hasNext()) { 60 | String key = keys.next(); 61 | JSONArray jsonArray = json.getJSONArray(key); 62 | List values = Lists.newArrayList(); 63 | for (int i = 0; i < jsonArray.length(); i++) 64 | values.add(jsonArray.getString(i)); 65 | data.put(key, values); 66 | } 67 | } 68 | 69 | public JSONObject getJSON() throws JSONException { 70 | JSONObject json = new JSONObject(); 71 | json.put("name", name); 72 | json.put("owner", owner); 73 | json.put("data", new JSONObject(data)); 74 | 75 | return json; 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | try { 81 | return getJSON().toString(); 82 | } 83 | catch (Exception e) { 84 | return e.getMessage(); 85 | } 86 | } 87 | 88 | public String getDisplayName() { 89 | return "Application Group " + name; 90 | } 91 | 92 | public String getLink() { 93 | return "dashboard/appgroup#appgroup=" + name; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/com/netflix/ice/basic/EddaResourceServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.ice.basic; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Properties; 6 | 7 | import org.junit.Test; 8 | 9 | import com.amazonaws.util.json.JSONArray; 10 | import com.netflix.ice.tag.Product; 11 | 12 | 13 | /** 14 | * Note: These tests require a running instance of Edda running at the url configured in ice.properties, so they 15 | * strictly speaking not unit tests any more... 16 | */ 17 | public class EddaResourceServiceTest { 18 | 19 | @Test 20 | public void test() throws Exception { 21 | EddaResourceService service = new EddaResourceService(new Properties()); 22 | 23 | service.init(); 24 | 25 | // does nothing really... 26 | service.commit(); 27 | 28 | assertNotNull(service.getProductsWithResources()); 29 | 30 | assertEquals("Product-name for unsupported resource", "somename", service.getResource(null, null, new Product("somename"), null, null, 0)); 31 | assertEquals("Error for empty resourceId", "Error", service.getResource(null, null, Product.ec2, null, null, 0)); 32 | assertEquals("Error for empty resourceId", "Error", service.getResource(null, null, Product.ec2, "", null, 0)); 33 | 34 | assertEquals("Unknown for resourceIds that we do not find", "Unknown", service.getResource(null, null, Product.ec2, "someunknowninstance", null, 0)); 35 | 36 | JSONArray instances = service.readInstanceArray(); 37 | 38 | String resource = service.getResource(null, null, Product.ec2, instances.getString(0), null, 0); 39 | assertFalse("Not Error for an actual instance", "Error".equals(resource)); 40 | 41 | resource = service.getResource(null, null, Product.ec2_instance, instances.getString(0), null, 0); 42 | assertFalse("Not Error for an actual instance", "Error".equals(resource)); 43 | 44 | for(int i = 0;i < instances.length();i++) { 45 | resource = service.getResource(null, null, Product.ec2_instance, instances.getString(i), null, 0); 46 | assertFalse("Not Error for an actual instance", "Error".equals(resource)); 47 | } 48 | } 49 | 50 | @Test 51 | public void testWrongURL() throws Exception { 52 | // use a normal setup for retrieving the instances 53 | EddaResourceService service = new EddaResourceService(new Properties()); 54 | JSONArray instances = service.readInstanceArray(); 55 | 56 | // overwrite config with an invalid hostname 57 | Properties prop = new Properties(); 58 | prop.setProperty("ice.eddaresourceservice.url", "http://invalidhostname:18081/edda/api/v2/"); 59 | service = new EddaResourceService(prop); 60 | 61 | // now the retrieved resources should return an error even for valid instances 62 | String resource = service.getResource(null, null, Product.ec2, instances.getString(0), null, 0); 63 | assertTrue("Error even for an actual instance when using wrong URL", "Error".equals(resource)); 64 | 65 | // overwrite config with an invalid URL 66 | prop.setProperty("ice.eddaresourceservice.url", "sasie://invalidhostname:18081/edda/api/v2/"); 67 | service = new EddaResourceService(prop); 68 | 69 | // now the retrieved resources should return an error even for valid instances 70 | resource = service.getResource(null, null, Product.ec2, instances.getString(0), null, 0); 71 | assertTrue("Error even for an actual instance when using wrong URL", "Error".equals(resource)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/Config.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | import com.amazonaws.auth.AWSCredentialsProvider; 21 | import org.joda.time.DateTime; 22 | import org.joda.time.DateTimeZone; 23 | 24 | import java.util.Properties; 25 | 26 | public abstract class Config { 27 | 28 | public final String workS3BucketName; 29 | public final String workS3BucketPrefix; 30 | public final String localDir; 31 | public final AccountService accountService; 32 | public final ProductService productService; 33 | public final ResourceService resourceService; 34 | public final DateTime startDate; 35 | public final AWSCredentialsProvider credentialsProvider; 36 | 37 | /** 38 | * 39 | * @param properties (required) 40 | * @param credentialsProvider (required) 41 | * @param accountService (required) 42 | * @param productService (required) 43 | * @param resourceService (optional) 44 | */ 45 | public Config( 46 | Properties properties, 47 | AWSCredentialsProvider credentialsProvider, 48 | AccountService accountService, 49 | ProductService productService, 50 | ResourceService resourceService) { 51 | if (properties.getProperty(IceOptions.START_MILLIS) == null) throw new IllegalArgumentException("IceOptions.START_MILLIS must be specified"); 52 | if (properties == null) throw new IllegalArgumentException("properties must be specified"); 53 | if (credentialsProvider == null) throw new IllegalArgumentException("credentialsProvider must be specified"); 54 | if (accountService == null) throw new IllegalArgumentException("accountService must be specified"); 55 | if (productService == null) throw new IllegalArgumentException("productService must be specified"); 56 | 57 | DateTime startDate = new DateTime(Long.parseLong(properties.getProperty(IceOptions.START_MILLIS)), DateTimeZone.UTC); 58 | workS3BucketName = properties.getProperty(IceOptions.WORK_S3_BUCKET_NAME); 59 | workS3BucketPrefix = properties.getProperty(IceOptions.WORK_S3_BUCKET_PREFIX, "ice/"); 60 | localDir = properties.getProperty(IceOptions.LOCAL_DIR, "/mnt/ice"); 61 | 62 | if (workS3BucketName == null) throw new IllegalArgumentException("IceOptions.WORK_S3_BUCKET_NAME must be specified"); 63 | 64 | this.credentialsProvider = credentialsProvider; 65 | this.startDate = startDate; 66 | this.accountService = accountService; 67 | this.productService = productService; 68 | this.resourceService = resourceService; 69 | 70 | AwsUtils.init(credentialsProvider); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/common/ResourceService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.common; 19 | 20 | import com.netflix.ice.tag.Account; 21 | import com.netflix.ice.tag.Product; 22 | import com.netflix.ice.tag.Region; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * Please see a sample of subclass in SampleMapDbResourceService class. 28 | */ 29 | public abstract class ResourceService { 30 | 31 | /** 32 | * Subclass can choose different technologies to store the mapping of resource ids and resource group names. 33 | * E.g. SampleMapDbResourceService used MapDB to maintain a mapping of instance ids and application names. 34 | * You can initialize you mapping here. 35 | * You can also get the reference to ProcessorConfig instance here. 36 | */ 37 | abstract public void init(); 38 | 39 | /** 40 | * Get resource group name. Subclass can maintain a mapping of resource ids and resource group names. 41 | * Users can also choose to add user-defined tags in the billing file. E.g. in SampleMapDbResourceService, 42 | * the auto-scaling-group name is used to generate the resource group name. 43 | * @param account 44 | * @param region 45 | * @param product 46 | * @param resourceId: depending on product, resourceId could be: 47 | * 1) instance id, if product is ec2_instance. You can use Edda (https://github.com/Netflix/edda) to query application name from instance id. 48 | * 2) volumn id, if product is ebs. You can use Edda (https://github.com/Netflix/edda) to query instance id from volumn id, then application name from instance id. 49 | * 3) s3 bucket name if product is s3 50 | * 4) db name if product is rds 51 | * 5) etc. 52 | * @param lineItem: the line item in the billing file. You can access your user-defined tags here. 53 | * @param millisStart 54 | * @return 55 | */ 56 | public String getResource(Account account, Region region, Product product, String resourceId, String[] lineItem, long millisStart){ 57 | return product.name; 58 | } 59 | 60 | /** 61 | * Get products with resources. See example in SampleMapDbResourceService. This method will be used by UI to list 62 | * products with resources. 63 | * @return List of list of products. The inner list of products share the same resource groups. 64 | * E.g. in SampleMapDbResourceService, for products (ec2, ec2_instance, ebs), the resource group names are application names. 65 | */ 66 | abstract public List> getProductsWithResources(); 67 | 68 | /** 69 | * Commit resource mappings. This method will be called at the end of billing file processing to commit your mappings. 70 | */ 71 | abstract public void commit(); 72 | } 73 | -------------------------------------------------------------------------------- /src/java/com/netflix/ice/tag/UsageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.ice.tag; 19 | 20 | import com.google.common.collect.Lists; 21 | import com.google.common.collect.Maps; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.io.DataInput; 26 | import java.io.DataOutput; 27 | import java.io.IOException; 28 | import java.util.List; 29 | import java.util.concurrent.ConcurrentMap; 30 | 31 | public class UsageType extends Tag { 32 | private static final Logger logger = LoggerFactory.getLogger(Operation.class); 33 | public final String unit; 34 | 35 | private UsageType (String name, String unit) { 36 | super(name); 37 | this.unit = unit; 38 | } 39 | private static ConcurrentMap usageTypes = Maps.newConcurrentMap(); 40 | 41 | public static void serialize(DataOutput out, UsageType usageType) throws IOException { 42 | out.writeUTF(usageType.name); 43 | out.writeUTF(usageType.unit); 44 | } 45 | 46 | public static UsageType deserialize(DataInput in) throws IOException { 47 | String name = in.readUTF(); 48 | String unit = in.readUTF(); 49 | 50 | UsageType usageType = usageTypes.get(name); 51 | if (usageType == null) { 52 | usageTypes.putIfAbsent(name, new UsageType(name, unit)); 53 | usageType = usageTypes.get(name); 54 | } 55 | else if (!usageType.unit.equals(unit)) { 56 | logger.error("found different units for " + usageType + usageType.unit + " " + unit); 57 | } 58 | return usageType; 59 | } 60 | 61 | public static UsageType getUsageType(String name, Operation operation, String description) { 62 | String unit = ""; 63 | if (name.contains("Bytes") || name.contains("ByteHrs") || description.contains("GB")) 64 | unit = "GB"; 65 | if (operation instanceof Operation.ReservationOperation) 66 | unit = "hours"; 67 | 68 | return getUsageType(name, unit); 69 | } 70 | 71 | public static UsageType getUsageType(String name, String unit) { 72 | 73 | UsageType usageType = usageTypes.get(name); 74 | if (usageType == null) { 75 | usageTypes.putIfAbsent(name, new UsageType(name, unit)); 76 | usageType = usageTypes.get(name); 77 | } 78 | else if (!usageType.unit.equals(unit)) { 79 | logger.error("found different units for " + usageType + usageType.unit + " " + unit); 80 | } 81 | return usageType; 82 | } 83 | 84 | public static List getUsageTypes(List names) { 85 | List result = Lists.newArrayList(); 86 | for (String name: names) 87 | result.add(usageTypes.get(name)); 88 | return result; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /grails-app/views/dashboard/editappgroup.gsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | 3 | Copyright 2013 Netflix, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | --%> 18 | 19 | <%@ page contentType="text/html;charset=UTF-8" %> 20 | 21 | 22 | 23 | Edit Application Group 24 | 25 | 26 | 27 | 28 | {{message}} 29 | 30 | CreateEdit Application Group {{appgroup.name}} 31 | 32 | 33 | 34 | 35 | Group Name: 36 | 37 | 38 | 39 | 40 | Email: 41 | 42 | 43 | 44 | 45 | Selected {{row.displayName}}: 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Rest of {{row.displayName}}: 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Save 63 | Delete 64 | Cancel 65 | 66 | 67 | 68 | 69 | 70 |